Merge remote-tracking branch 'origin/main' into room-takeover
commit
ee199b2dff
@ -0,0 +1,152 @@
|
||||
# MSC2246: Asynchronous media uploads
|
||||
Sending media to Matrix currently requires that clients first upload the media
|
||||
to the content repository and then send the event. This is a problem for some
|
||||
use cases, such as bridges that want to preserve message order, as reuploading
|
||||
a large file would block all messages.
|
||||
|
||||
## Proposal
|
||||
This proposal proposes a way to send the event containing media before actually
|
||||
uploading the media, which would make the aforementioned bridge message order
|
||||
preservation possible without blocking all other messages behind a long upload.
|
||||
|
||||
In the future, this new functionality could be used for streaming file
|
||||
transfers, as requested in [matrix-spec#432].
|
||||
|
||||
### Content repository behavior
|
||||
The proposal adds two new endpoints to the content repository API and modifies
|
||||
the download and thumbnail endpoints.
|
||||
|
||||
#### `POST /_matrix/media/v1/create`
|
||||
Create a new MXC URI without content. Like `/upload`, this endpoint requires
|
||||
auth, can be rate limited, and returns the `content_uri` that can be used in
|
||||
events.
|
||||
|
||||
The request body should be an empty JSON object. In the future, the body could
|
||||
be used for metadata about the file, such as the mime type or access control
|
||||
settings (related: [MSC701]).
|
||||
|
||||
The server may optionally enforce a maximum age for unused media IDs to delete
|
||||
media IDs when the client doesn't start the upload in time, or when the upload
|
||||
was interrupted and not resumed in time. The server should include the maximum
|
||||
POSIX millisecond timestamp to complete the upload in the `unused_expires_at`
|
||||
field in the response JSON. The recommended default expiration is 24 hours which
|
||||
should be enough time to accommodate users on poor connection who find a better
|
||||
connection to complete the upload.
|
||||
|
||||
##### Rate Limiting
|
||||
|
||||
The server should rate limit requests to create media.
|
||||
|
||||
The server should limit the number of concurrent *pending media uploads* a given
|
||||
user can have. A pending media upload is a created MXC URI that (a) is not
|
||||
expired (the `unused_expires_at` timestamp has not passed) and (b) the media has
|
||||
not yet been uploaded for.
|
||||
|
||||
In both cases, the server should respond with `M_LIMIT_EXCEEDED` optionally
|
||||
providing details in the `error` field, but servers may wish to obscure the
|
||||
exact limits that are used and not provide such details.
|
||||
|
||||
##### Example response
|
||||
```json
|
||||
{
|
||||
"content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw",
|
||||
"unused_expires_at": 1647257217083
|
||||
}
|
||||
```
|
||||
|
||||
#### `PUT /_matrix/media/v3/upload/{serverName}/{mediaId}`
|
||||
Upload content to a MXC URI that was created earlier. This endpoint requires
|
||||
auth. If the upload is successful, an empty JSON object and status code 200 is
|
||||
returned. Rate limiting additionally can apply here.
|
||||
|
||||
If the endpoint is called with a media ID that already has content, the request
|
||||
should be rejected with the error code `M_CANNOT_OVERWRITE_MEDIA` and HTTP
|
||||
status code 409.
|
||||
|
||||
If the upload request comes from a user other than the one who created the media
|
||||
ID, the request should be rejected with an `M_FORBIDDEN` error.
|
||||
|
||||
If the serverName/mediaId combination is not known, not local, or expired, an
|
||||
`M_NOT_FOUND` error is returned.
|
||||
|
||||
If the MXC's `unused_expires_at` is reached before the upload completes, the
|
||||
server may either respond immediately with `M_NOT_FOUND` or allow the upload to
|
||||
continue.
|
||||
|
||||
For other errors, such as file size, file type or user quota errors, the normal
|
||||
`/upload` rules apply.
|
||||
|
||||
#### Changes to the `/download` and `/thumbnail` endpoints
|
||||
A new query parameter, `timeout_ms` is added to the endpoints that can
|
||||
download media. It's an integer that specifies the maximum number of
|
||||
milliseconds that the client is willing to wait to start receiving data.
|
||||
The default value is 20000 (20 seconds). The content repository can and should
|
||||
impose a maximum value for this parameter. The content repository can also
|
||||
choose to respond before the timeout if it desires.
|
||||
|
||||
If the media is available immediately (for example in the case of a
|
||||
non-asynchronous upload), the content repository should ignore this parameter.
|
||||
|
||||
If the MXC has expired, the content repository should respond with `M_NOT_FOUND`
|
||||
and a HTTP 404 status code.
|
||||
|
||||
If the data is not available when the server chooses to respond, the content
|
||||
repository returns a `M_NOT_YET_UPLOADED` error with a HTTP 504 status code.
|
||||
|
||||
For the `/download` endpoint, the server could also stream data directly as it
|
||||
is being uploaded. However, streaming creates several implementation and spec
|
||||
complications (e.g. how to stream if the media repo has multiple workers, what
|
||||
to do if the upload is interrupted), so specifying exactly how streaming works
|
||||
is left for another MSC.
|
||||
|
||||
## Potential issues
|
||||
Other clients may time out the download if the sender takes too long to upload
|
||||
media.
|
||||
|
||||
## Alternatives
|
||||
|
||||
## Security considerations
|
||||
|
||||
The primary attack vector that must be prevented is a malicious user creating a
|
||||
large number of MXC URIs and sending them to a room without uploading the
|
||||
corresponding media. Clients in that room would then attempt to download the
|
||||
media, holding open connections to the server and potentially exhausting the
|
||||
number of available connections.
|
||||
|
||||
This attack vector is stopped in multiple ways:
|
||||
|
||||
1. Limits on `/create` prevent users from creating MXC URIs too quickly and also
|
||||
require them to finish uploading files (or let some of their MXCs expire)
|
||||
before creating new MXC URIs.
|
||||
|
||||
2. Servers are free to respond to `/download` and `/thumbnail` requests before
|
||||
the `timeout_ms` has been reached and respond with `M_NOT_YET_UPLOADED`. For
|
||||
example, if the server is under connection count pressure, it can choose to
|
||||
respond to waiting download connections with `M_NOT_YET_UPLOADED` to free
|
||||
connections in the pool.
|
||||
|
||||
3. Once the media is expired, servers can respond immediately to `/download` and
|
||||
`/thumbnail` requests with `M_NOT_FOUND`.
|
||||
|
||||
## Future work
|
||||
|
||||
Future MSCs might wish to address large file uploads. One approach would be to
|
||||
add metadata to the `/create` call via a query parameter (for example
|
||||
`?large_file_upload=true`. Servers would have the ability to impose restrictions
|
||||
on how many such "large file" uploads a user can have concurrently. For such a
|
||||
situation, the server would likely send a more generous `unused_expires_at`
|
||||
timestamp to allow for a long-running upload.
|
||||
|
||||
## Unstable prefix
|
||||
While this MSC is not in a released version of the spec, implementations should
|
||||
use `fi.mau.msc2246` as a prefix and as an `unstable_features` flag in the
|
||||
`/versions` endpoint.
|
||||
|
||||
* `POST /_matrix/media/unstable/fi.mau.msc2246/create`
|
||||
* `PUT /_matrix/media/unstable/fi.mau.msc2246/upload/{serverName}/{mediaId}`
|
||||
* `?fi.mau.msc2246.timeout_ms`
|
||||
* `FI.MAU.MSC2246_NOT_YET_UPLOADED`
|
||||
* `FI.MAU.MSC2246_CANNOT_OVERWRITE_MEDIA`
|
||||
|
||||
[matrix-spec#432]: https://github.com/matrix-org/matrix-spec/issues/432
|
||||
[MSC701]: https://github.com/matrix-org/matrix-doc/issues/701
|
||||
@ -0,0 +1,54 @@
|
||||
# MSC2249: Require users to have visibility on an event when submitting reports
|
||||
|
||||
The [report API](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-rooms-roomid-report-eventid)
|
||||
currently does not require users to be joined to the room in order to report that an
|
||||
event is inappropriate. This allows anyone to report any event in any room without being joined to the room.
|
||||
There is limited use (and scope for abuse) for users to call report on rooms they are not joined to,
|
||||
so this proposal requires that reporting users must be joined to a room before they can report an event.
|
||||
|
||||
Furthermore this proposal addresses the case where the user may not have visibility
|
||||
on an event (e.g. not being able to read history in a room).
|
||||
|
||||
In that case, similar logic applies as described below.
|
||||
|
||||
## Proposal
|
||||
|
||||
The `/rooms/{roomId}/report/{eventId}` endpoint should check to see if the authenticated user
|
||||
is joined to the room in the current state of the room. If the user is not joined to the room,
|
||||
the room does not exist, or the event does not exist the server should respond with:
|
||||
|
||||
```json
|
||||
{
|
||||
"errcode": "M_NOT_FOUND",
|
||||
"error": "Unable to report event: it does not exist or you aren't able to see it."
|
||||
}
|
||||
```
|
||||
|
||||
where the contents of `error` can be left to the implementation. It is important to note that this response
|
||||
MUST be sent regardless if the room/event exists or not as this endpoint could be used as a way to brute
|
||||
force room/event IDs in order to find a room/event.
|
||||
|
||||
It is not expected for homeservers to attempt to backfill an event they cannot find locally, as the user is unlikely to
|
||||
have seen an event that the homeserver has not yet stored.
|
||||
|
||||
If the event is redacted, reports MAY still be allowed but are dependant on the implementation.
|
||||
|
||||
## Tradeoffs
|
||||
|
||||
None
|
||||
|
||||
## Potential issues
|
||||
|
||||
This will incur a performance penalty on the endpoint as the homeserver now needs to query state in the room, however
|
||||
this is considered acceptable given the potential to reduce abuse of the endpoint.
|
||||
|
||||
## Security considerations
|
||||
|
||||
Care should be taken not to give away information inadvertently by responding with different error codes depending
|
||||
on the existence of the room, as it may give away private rooms on the homeserver. This may be somewhat unavoidable
|
||||
due to the time delay for checking the existence of a room vs checking the state for a user, so implementations
|
||||
MAY decide to "fuzz" the response times of the endpoint to avoid time-based attacks.
|
||||
## Conclusion
|
||||
|
||||
This proposal should hopefully reduce the abuse potential of the /report endpoint without significantly increasing
|
||||
the complexity or performance requirements on a homeserver.
|
||||
@ -0,0 +1,105 @@
|
||||
# MSC2659: Application service ping endpoint
|
||||
|
||||
## Problem
|
||||
A relatively common problem when setting up appservices is the connection
|
||||
between the appservice and homeserver not working in one or both directions.
|
||||
If the appservice is unable to connect to the homeserver, it can simply show
|
||||
the error message to the user. However, there's currently no easy way for the
|
||||
appservice to know if the homeserver is unable to connect to it. This means
|
||||
that the appservice might start up fine, but not actually work, because the
|
||||
homeserver isn't sending events to it.
|
||||
|
||||
## Proposed solution
|
||||
The proposed solution is a new endpoint in homeservers that appservices can use
|
||||
to trigger a ping. A new endpoint is also added to the appservice side for the
|
||||
homeserver to call without any side-effects.
|
||||
|
||||
Appservices can use the endpoint at startup to ensure communication works in
|
||||
both directions, and show an error to the user if it doesn't.
|
||||
|
||||
### `POST /_matrix/app/v1/ping`
|
||||
This endpoint is on the appservice side. Like all other appservice-side
|
||||
endpoints, it is authenticated using the `hs_token`. When the token is correct,
|
||||
this returns HTTP 200 and an empty JSON object as the body.
|
||||
|
||||
The request body contains an optional `transaction_id` string field, which
|
||||
comes from the client ping request defined below.
|
||||
|
||||
Appservices don't need to have any special behavior on this endpoint, but they
|
||||
may use the incoming request to verify that an outgoing ping actually pinged
|
||||
the appservice rather than going somewhere else.
|
||||
|
||||
This proposal doesn't define any cases where a homeserver would call the ping
|
||||
endpoint unless explicitly requested by the appservice (using the client
|
||||
endpoint below). Therefore, appservices don't necessarily have to implement
|
||||
this endpoint if they never call the client ping endpoint.
|
||||
|
||||
### `POST /_matrix/client/v1/appservice/{appserviceId}/ping`
|
||||
When the endpoint is called, the homeserver makes a `/_matrix/app/v1/ping`
|
||||
request to the appservice.
|
||||
|
||||
The request body may contain a `transaction_id` string field, which, if present,
|
||||
must be passed through to the appservice `/ping` request body as-is.
|
||||
|
||||
This endpoint is only allowed when using a valid appservice token, and it can
|
||||
only ping the appservice associated with the token. If the token or appservice
|
||||
ID in the path is wrong, the server may return `M_FORBIDDEN`. However,
|
||||
implementations and future spec proposals may extend what kinds of pings are
|
||||
allowed.
|
||||
|
||||
In case the homeserver had backed off on sending transactions, it may treat a
|
||||
successful ping as a sign that the appservice is up again and transactions
|
||||
should be retried.
|
||||
|
||||
#### Response
|
||||
If the ping request returned successfully, the endpoint returns HTTP 200. The
|
||||
response body has a `duration_ms` field containing the `/_matrix/app/v1/ping`
|
||||
request roundtrip time as milliseconds.
|
||||
|
||||
If the request fails, the endpoint returns a standard error response with
|
||||
`errcode`s and HTTP status codes as specified below:
|
||||
|
||||
* If the appservice doesn't have a URL configured, `M_URL_NOT_SET` and HTTP 400.
|
||||
* For non-2xx responses, `M_BAD_STATUS` and HTTP 502. Additionally, the response
|
||||
may include `status` (integer) and `body` (string) fields containing the HTTP
|
||||
status code and response body text respectively to aid with debugging.
|
||||
* For connection timeouts, `M_CONNECTION_TIMEOUT` and HTTP 504.
|
||||
* For other connection errors, `M_CONNECTION_FAILED` and HTTP 502.
|
||||
It is recommended to put a more detailed explanation in the `error` field.
|
||||
|
||||
### Example flow
|
||||
|
||||
1. bridge -> homeserver (request #1): `POST http://synapse:8008/_matrix/client/v1/appservice/whatsapp/ping`
|
||||
* Header `Authorization: Bearer as_token`
|
||||
* Body: `{"transaction_id": "meow"}`
|
||||
2. homeserver -> bridge (request #2): `POST http://bridge:29318/_matrix/app/v1/ping`
|
||||
* Header `Authorization: Bearer hs_token`
|
||||
* Body: `{"transaction_id": "meow"}`
|
||||
3. bridge -> homeserver (response to #2): 200 OK with body `{}`
|
||||
4. homeserver -> bridge (response to #1): 200 OK with body `{"duration_ms": 123}`
|
||||
(123 milliseconds being the time it took for request #2 to complete).
|
||||
|
||||
## Alternatives
|
||||
|
||||
* The ping could make an empty `/transactions` request instead of adding a new
|
||||
ping endpoint. A new endpoint was found to be cleaner while implementing, and
|
||||
there didn't seem to be any significant benefits to reusing transactions.
|
||||
* A `/versions` endpoint could be introduced to work for both pinging and
|
||||
checking what spec versions an appservice supports. However, it's not clear
|
||||
that a new endpoint is the best way to detect version support (a simple flag
|
||||
in the registration file may be preferable), so this MSC proposes a `/ping`
|
||||
endpoint that doesn't have other behavior.
|
||||
* Appservices could be switched to using websockets instead of the server
|
||||
pushing events. This option is already used by some bridges, but implementing
|
||||
websocket support on the homeserver side is much more complicated than a
|
||||
simple ping endpoint.
|
||||
|
||||
## Unstable prefix
|
||||
The endpoints can be implemented as `/_matrix/app/unstable/fi.mau.msc2659/ping`
|
||||
and `/_matrix/client/unstable/fi.mau.msc2659/appservice/{appserviceId}/ping`.
|
||||
Error codes can use `FI.MAU.MSC2659_` instead of `M_` as the prefix.
|
||||
|
||||
`fi.mau.msc2659` can be used as an `unstable_features` flag in `/versions` to
|
||||
indicate support for the unstable prefixed endpoint. Once the MSC is approved,
|
||||
`fi.mau.msc2659.stable` can be used to indicate support for the stable endpoint
|
||||
until the spec release containing the endpoint is supported.
|
||||
@ -0,0 +1,345 @@
|
||||
# MSC2746: Improved Signalling for 1:1 VoIP
|
||||
|
||||
Historically, Matrix has basic support for signalling 1:1 WebRTC calls which suffer a number of shortcomings:
|
||||
|
||||
* If several devices try to answer the same call, there is no way for them to determine clearly
|
||||
that the caller has set up the call with a different device, and no way for the caller to
|
||||
determine which candidate events map to which answer.
|
||||
* Hangup reasons are often incorrect.
|
||||
* There is confusion and no clear guidance on how clients should determine whether an incoming
|
||||
invite is stale or not.
|
||||
* There is no support for renegotiation of SDP, for changing ICE candidates / hold/resume
|
||||
functionality, etc.
|
||||
* There is no distinction between rejecting a call and ending it, which means that in trying
|
||||
to reject a call, a client can inadvertently cause a call that has been successfully set up
|
||||
on a different device to be hung up.
|
||||
|
||||
## Proposal
|
||||
### Change the `version` field in all VoIP events to `"1"`
|
||||
The version property is changed to `"1"` in all existing VoIP events
|
||||
([`m.call.answer`](https://spec.matrix.org/v1.5/client-server-api/#mcallanswer),
|
||||
[`m.call.candidates`](https://spec.matrix.org/v1.5/client-server-api/#mcallcandidates),
|
||||
[`m.call.hangup`](https://spec.matrix.org/v1.5/client-server-api/#mcallhangup)
|
||||
[`m.call.invite`](https://spec.matrix.org/v1.5/client-server-api/#mcallinvite)). Note
|
||||
that this changes the type of the `version` field from an integer to a string, as
|
||||
described in the [Unstable Prefix](#unstable-prefix) section.
|
||||
|
||||
This will be used to determine whether devices support this new version of the protocol. For example,
|
||||
clients can use this field to know whether to expect an `m.call.select_answer` event from their
|
||||
opponent. If clients see events with `version` other than `0` or `"1"` (including, for example, the numeric
|
||||
value `1`), they should treat these the same as if they had `version` == `"1"`.
|
||||
|
||||
Note that this implies any and all future versions of VoIP events should be backwards-compatible.
|
||||
If it does become necessary to introduce a non backwards-compatible VoIP spec, the intention would
|
||||
be for it to simply use a separate set of event types.
|
||||
|
||||
### Define the configurations of WebRTC streams and tracks
|
||||
|
||||
The [spec](https://spec.matrix.org/v1.5/client-server-api/#voice-over-ip) does not currently define
|
||||
the WebRTC streams and tracks that should be sent. Under this proposal,
|
||||
clients are expected to send one stream with one track of kind `audio` (creating a
|
||||
voice call). They can optionally send a second track in the same stream of kind
|
||||
`video` (creating a video call).
|
||||
|
||||
Clients implementing this specification use the first stream and will ignore any streamless tracks. Note that
|
||||
in the Javascript WebRTC API, this means `addTrack()` must be passed two parameters: a track and a stream,
|
||||
not just a track, and in a video call the stream must be the same for both audio and video track.
|
||||
|
||||
A client may send other streams and tracks but the behaviour of the other party with respect to presenting
|
||||
such streams and tracks is undefined.
|
||||
|
||||
This follows the existing known implementations of v0 VoIP.
|
||||
|
||||
### Add `invitee` field to [`m.call.invite`](https://spec.matrix.org/v1.5/client-server-api/#mcallinvite)
|
||||
This allows for the following use cases:
|
||||
* Placing a call to a specific user in a room where other users are also present.
|
||||
* Placing a call to oneself.
|
||||
|
||||
The field should be added for all invites where the target is a specific user, and should be set
|
||||
to the Matrix user ID of that user. Invites without an `invitee`
|
||||
field are defined to be intended for any member of the room other than the sender of the event.
|
||||
|
||||
Clients should consider an incoming call if they see a non-expired invite event where the `invitee` field is either
|
||||
absent or equal to their user's Matrix ID, however they should evaluate whether or not to ring based on their
|
||||
user's trust relationship with the callers and/or where the call was placed. As a starting point, it is
|
||||
suggested that clients ignore call invites from users in public rooms. It is strongly recommended that
|
||||
when clients do not ring for an incoming call invite, they still display the call invite in the room and
|
||||
annotate that it was ignored.
|
||||
|
||||
### Add `party_id` to all VoIP events
|
||||
Whenever a client first participates in a new call, it generates a `party_id` for itself to use for the
|
||||
duration of the call. This needs to be long enough that the chance of a collision between multiple devices
|
||||
both generating an answer at the same time generating the same party ID is vanishingly small: 8 uppercase +
|
||||
lowercase alphanumeric characters is recommended. Parties in the call are identified by the tuple of
|
||||
`(user_id, party_id)`.
|
||||
|
||||
The client adds a `party_id` field containing this ID to the top-level of the content of all VoIP events
|
||||
it sends on the call, including `m.call.invite`. Clients use this to identify remote echo of their own
|
||||
events: since a user may now call themselves, they can no longer ignore events from their own user. This
|
||||
field also identifies different answers sent by different clients to an invite, and matches `m.call.candidates`
|
||||
events to their respective answer/invite.
|
||||
|
||||
A client implementation may choose to use the device ID used in end-to-end cryptography for this purpose,
|
||||
or it may choose, for example, to use a different one for each call to avoid leaking information on which
|
||||
devices were used in a call (in an unencrypted room) or if a single device (ie. access token) were used to
|
||||
send signalling for more than one call party.
|
||||
|
||||
A grammar for `party_id` is defined [below](#specify-exact-grammar-for-voip-ids).
|
||||
|
||||
### Introduce `m.call.select_answer`
|
||||
This event is sent by the caller's client once it has decided which other
|
||||
client to talk to, by selecting one of multiple possible incoming `m.call.answer`
|
||||
events. Its `selected_party_id` field indicates the answer it's chosen. The `call_id`
|
||||
and `party_id` of the caller is also included. If the callee's client sees a `select_answer` for an answer
|
||||
with party ID other than the one it sent, it ends the call and informs the user the call
|
||||
was answered elsewhere. It does not send any events. Media can start flowing
|
||||
before this event is seen or even sent. Clients that implement previous
|
||||
versions of this specification will ignore this event and behave as they did
|
||||
before.
|
||||
|
||||
Example:
|
||||
```
|
||||
{
|
||||
"type": "m.call.select_answer",
|
||||
"content": {
|
||||
"version": "1",
|
||||
"call_id": "12345",
|
||||
"party_id": "67890",
|
||||
"selected_party_id": "111213",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Introduce `m.call.reject`
|
||||
|
||||
* If the `m.call.invite` event has `version` `"1"`, a client wishing to reject the call
|
||||
sends an `m.call.reject` event. This rejects the call on all devices, but if the calling
|
||||
device sees an `answer` before the `reject`, it disregards the reject event and carries on. The reject has a
|
||||
`party_id` just like an answer, and the caller sends a `select_answer` for it just like an
|
||||
answer. If another client had already sent an answer and sees the caller select the
|
||||
reject response instead of its answer, it ends the call.
|
||||
* If the `m.call.invite` event has `version` `0`, the callee sends an `m.call.hangup` event.
|
||||
|
||||
Example:
|
||||
```
|
||||
{
|
||||
"type": "m.call.reject",
|
||||
"content" : {
|
||||
"version": "1",
|
||||
"call_id": "12345",
|
||||
"party_id": "67890",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the calling user chooses to end the call before setup is complete, the client sends `m.call.hangup`
|
||||
as previously.
|
||||
|
||||
### Clarify what actions a client may take in response to an invite
|
||||
The client may:
|
||||
* Attempt to accept the call by sending an `m.call.answer`.
|
||||
* Actively reject the call everywhere: send an `m.call.reject` as per above, which will stop the call from
|
||||
ringing on all the user's devices and the caller's client will inform them that the user has
|
||||
rejected their call.
|
||||
* Ignore the call: send no events, but stop alerting the user about the call. The user's other
|
||||
devices will continue to ring, and the caller's device will continue to indicate that the call
|
||||
is ringing, and will time the call out in the normal way if no other device responds.
|
||||
|
||||
### Introduce more reason codes to [`m.call.hangup`](https://spec.matrix.org/v1.5/client-server-api/#mcallhangup)
|
||||
* `ice_timeout`: The connection failed after some media was exchanged (as opposed to current
|
||||
`ice_failed` which means no media connection could be established). Note that, in the case of
|
||||
an ICE renegotiation, a client should be sure to send `ice_timeout` rather than `ice_failed` if
|
||||
media had previously been received successfully, even if the ICE renegotiation itself failed.
|
||||
* `user_hangup`: Clients must now send this code when the user chooses to end the call, although
|
||||
for backwards compatibility with version 0, a clients should treat an absence of the `reason`
|
||||
field as `user_hangup`.
|
||||
* `user_media_failed`: The client was unable to start capturing media in such a way that it is unable
|
||||
to continue the call.
|
||||
* `user_busy`: The user is busy. Note that this exists primarily for bridging to other networks such
|
||||
as the PSTN. A Matrix client that receives a call whilst already in a call would not generally reject
|
||||
the new call unless the user had specifically chosen to do so.
|
||||
* `unknown_error`: Some other failure occurred that meant the client was unable to continue the call
|
||||
rather than the user choosing to end it.
|
||||
|
||||
### Introduce `m.call.negotiate`
|
||||
This introduces SDP negotiation semantics for media pause, hold/resume, ICE restarts and voice/video
|
||||
call up/downgrading. Clients should implement & honour hold functionality as per WebRTC's
|
||||
recommendation: https://www.w3.org/TR/webrtc/#hold-functionality
|
||||
|
||||
If both the invite event and the accepted answer event have `version` equal to `"1"`, either party may
|
||||
send `m.call.negotiate` with a `description` field to offer new SDP to the other party. This event has
|
||||
`call_id` with the ID of the call and `party_id` equal to the client's party ID for that call.
|
||||
The caller ignores any negotiate events with `party_id` + `user_id` tuple not equal to that of the
|
||||
answer it accepted and the callee ignores any negotiate events with `party_id` + `user_id` tuple not equal to that of the caller. Clients should use the `party_id` field to ignore the remote echo of their
|
||||
own negotiate events.
|
||||
|
||||
This has a `lifetime` field as in `m.call.invite`, after which the sender of the negotiate event
|
||||
should consider the negotiation failed (timed out) and the recipient should ignore it.
|
||||
|
||||
The `description` field is the same as the `offer` field in `m.call.invite` and `answer`
|
||||
field in `m.call.answer` and is an `RTCSessionDescriptionInit` object as per
|
||||
https://www.w3.org/TR/webrtc/#dom-rtcsessiondescriptioninit.
|
||||
|
||||
Example:
|
||||
```
|
||||
{
|
||||
"type": "m.call.negotiate",
|
||||
"content": {
|
||||
"version": "1",
|
||||
"call_id": "12345",
|
||||
"party_id": "67890",
|
||||
"lifetime": 10000,
|
||||
"description": {
|
||||
"sdp": "[some sdp]",
|
||||
"type": "offer",
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Once an `m.call.negotiate` event is received, the client must respond with another `m.call.negotiate`
|
||||
event, with the SDP answer (with `"type": "answer"`) in the `description` property.
|
||||
|
||||
This MSC also proposes clarifying the `m.call.invite` and `m.call.answer` events to state that
|
||||
the `offer` and `answer` fields respectively are objects of type `RTCSessionDescriptionInit`.
|
||||
Hence the `type` field, whilst redundant in these events, is included for ease of working
|
||||
with the WebRTC API and is mandatory. Receiving clients should not attempt to validate the `type` field,
|
||||
but simply pass the object into the WebRTC API.
|
||||
|
||||
### Designate one party as 'polite'
|
||||
In line with WebRTC perfect negotiation (https://w3c.github.io/webrtc-pc/#perfect-negotiation-example)
|
||||
we introduce rules to establish which party is polite in the process of renegotiation. The callee is
|
||||
always the polite party. In a glare situation, the politenes of a party is therefore determined by
|
||||
whether the inbound or outbound call is used: if a client discards its outbound call in favour of
|
||||
an inbound call, it becomes the polite party.
|
||||
|
||||
### Add explicit recommendations for call event liveness.
|
||||
`m.call.invite` contains a `lifetime` field that indicates how long the offer is valid for. When
|
||||
a client receives an invite, it should use the event's `age` field in the sync response plus the
|
||||
time since it received the event from the homeserver to determine whether the invite is still valid.
|
||||
The use of the `age` field ensures that incorrect clocks on client devices don't break calls.
|
||||
If the invite is still valid *and will remain valid for long enough for the user to accept the call*,
|
||||
it should signal an incoming call. The amount of time allowed for the user to accept the call may
|
||||
vary between clients. For example, it may be longer on a locked mobile device than on an unlocked
|
||||
desktop device.
|
||||
|
||||
The client should only signal an incoming call in a given room once it has completed processing the
|
||||
entire sync response and, for encrypted rooms, attempted to decrypt all encrypted events in the
|
||||
sync response for that room. This ensures that if the sync response contains subsequent events that
|
||||
indicate the call has been hung up, rejected, or answered elsewhere, the client does not signal it.
|
||||
|
||||
If on startup, after processing locally stored events, the client determines that there is an invite
|
||||
that is still valid, it should still signal it but only after it has completed a sync from the homeserver.
|
||||
|
||||
The minimal recommended lifetime is 90 seconds - this should give the user
|
||||
enough time to actually pick up the call.
|
||||
|
||||
### Introduce recommendations for batching of ICE candidates
|
||||
Clients should aim to send a small number of candidate events, with guidelines:
|
||||
* ICE candidates which can be discovered immediately or almost immediately in the invite/answer
|
||||
event itself (eg. host candidates). If server reflexive or relay candidates can be gathered in
|
||||
a sufficiently short period of time, these should be sent here too. A delay of around 200ms is
|
||||
suggested as a starting point.
|
||||
* The client should then allow some time for further candidates to be gathered in order to batch them,
|
||||
rather than sending each candidate as it arrives. A starting point of 2 seconds after sending the
|
||||
invite or 500ms after sending the answer is suggested as a starting point (since a delay is natural
|
||||
anyway after the invite whilst the client waits for the user to accept it).
|
||||
|
||||
### Mandate the end-of-candidates candidate
|
||||
Define that an ICE candidate whose value is the empty string means that no more ICE candidates will
|
||||
be sent, and mandate that clients must send such a candidate in an `m.call.candidates` message.
|
||||
The WebRTC spec requires browsers to generate such a candidate, however note that at time of writing,
|
||||
not all browsers do (Chrome does not, but does generate an `icegatheringstatechange` event). The
|
||||
client should send any remaining candidates once candidate generation finishes, ignoring timeouts above.
|
||||
This allows bridges to batch the candidates together when bridging to protocols that don't support
|
||||
trickle ICE.
|
||||
|
||||
### Add DTMF
|
||||
Add that Matrix clients can send DTMF as specified by WebRTC. The WebRTC standard as of August
|
||||
2020 does not support receiving DTMF but a Matrix client can receive and interpret the DTMF sent
|
||||
in the RTP payload.
|
||||
|
||||
### Specify exact grammar for VoIP IDs
|
||||
`call_id`s and the newly introduced `party_id` are explicitly defined to be between 1
|
||||
and 255 characters long, consisting of the characters `[0-9a-zA-Z._~-]`.
|
||||
|
||||
(Note that this matches the grammar of 'opaque IDs' from
|
||||
[MSC1597](https://github.com/matrix-org/matrix-spec-proposals/blob/rav/proposals/id_grammar/proposals/1597-id-grammar.md#opaque-ids),
|
||||
and that of the `id` property of the
|
||||
[`m.login.sso` flow schema](https://spec.matrix.org/v1.5/client-server-api/#definition-mloginsso-flow-schema).)
|
||||
|
||||
### Specify behaviour on room leave
|
||||
If the client sees the user it is in a call with leave the room, the client should treat this
|
||||
as a hangup event for any calls that are in progress. No specific requirement is given for the
|
||||
situation where a client has sent an invite and the invitee leaves the room, but the client may
|
||||
wish to treat it as a rejection if there are no more users in the room who could answer the call
|
||||
(eg. the user is now alone or the `invitee` field was set on the invite).
|
||||
|
||||
The same behaviour applies when a client is looking at historic calls.
|
||||
|
||||
### Clarify that supported codecs should follow the WebRTC spec
|
||||
The Matrix spec does not mandate particular audio or video codecs, but instead defers to the
|
||||
WebRTC spec. A compliant matrix VoIP client will behave in the same way as a supported 'browser'
|
||||
in terms of what codecs it supports and what variants thereof. The latest WebRTC specification
|
||||
applies, so clients should keep up to date with new versions of the WebRTC specification whether
|
||||
or not there have been any changes to the Matrix spec.
|
||||
|
||||
## Potential issues
|
||||
* The ability to call yourself makes the protocol a little more complex for clients to implement,
|
||||
and is somewhat of a special case. However, some of the necessary additions are also required for
|
||||
other features so this MSC elects to make it possible.
|
||||
* Clients must make a decision on whether to ring for any given call: defining this in the spec
|
||||
would be cumbersome and would limit clients' ability to use reputation-based systems for this
|
||||
decision in the future. However, having a call ring on one client and not the other because one
|
||||
had categorised it as a junk call and not the other would be confusing for the user.
|
||||
|
||||
## Alternatives
|
||||
* This MSC does not allow for ICE negotiation before the user chooses to answer the call. This can
|
||||
make call setup faster by allowing connectivity to be established whilst the call is ringing. This
|
||||
is problematic with Matrix since any device or user could answer the call, so it is not known which
|
||||
device is going to answer before the user chooses to answer. It would also leak information on which
|
||||
of a user's devices were online.
|
||||
* We could define that the ID of a call is implicitly the event ID of the invite event rather than
|
||||
having a specific `call_id` field. This would mean that a client would be unable to know the ID of
|
||||
a call before the it received the response from sending the invite event, which could complicate
|
||||
implementations. There is probably no compelling reason to change this.
|
||||
* `m.call.select_answer` was chosen such that its name reflect the intention of the event. `m.call.ack`
|
||||
is more succinct and mirrors SIP, but this MSC opts for the more descriptive name.
|
||||
* This MSC elects to allow invites without an `invitee` field to mean a call for anyone in the room.
|
||||
This could be useful for hunt group style semantics where an incoming call causes many different
|
||||
users' phones to ring and any one of them may pick up the call. This does mean clients will need
|
||||
to not blindly ring for any call invites in any room, since this would make unsolicited calls
|
||||
easy in public rooms. We could opt to leave this out, or make it more explicit with a specific value
|
||||
for the `invitee` field.
|
||||
* `party_id` is one of many potential solutions: callees could add `answer_id`s to their events and
|
||||
callers could be identified by the lack of an `answer_id`. An explicit field on every event may be
|
||||
easier to comprehend, less error-prone and clearer in the backwards-compatibility scenario.
|
||||
* We could make `party_id`s more prescriptive, eg. the caller could always have a `party_id` of the
|
||||
empty string, the word `caller` or equal to the `call_id`, which may make debugging simpler.
|
||||
* To allow for bridging into protocols that don't support trickle ICE, this proposal requires that
|
||||
clients send an empty candidate to signal the end of candidates. This means it will be up to bridges
|
||||
to buffer the invite and edit the SDP to add the candidates once they arrive, adding complexity to
|
||||
bridges. The alternative would be a discovery mechanism so clients could know whether a callee supports
|
||||
trickle ICE before calling, and disable it if so. This would add complexity to every Matrix client as
|
||||
well as having to assume that all current clients did not, disabling trickle ICE everywhere until clients
|
||||
support the discovery mechanism. The mechanism would also have to be per-user which would make sense for
|
||||
bridged users, but not where some of a users devices support trickle ICE and some do not.
|
||||
|
||||
## Security considerations
|
||||
* IP addresses remain in the room in candidates, as they did in the previous version of the spec.
|
||||
This is not ideal, but alternatives were either sending candidates over to-device messages
|
||||
(would slow down call setup because a target device would have to be established before sending
|
||||
candidates) or redacting them afterwards (the volume of events sent during calls can already
|
||||
cause rate limiting issues and this would exacerbate this).
|
||||
* Clients must take care to not ring for any call, as per the 'alternatives' section.
|
||||
|
||||
## Unstable prefix
|
||||
Since VoIP events already have a 'version' field, we would ideally use a string, namespaced version during
|
||||
development, but this field is defined to be an int in version 0. This MSC proposes changing the version
|
||||
field to a string so that this namespacing can be used for future changes. Since there is no other easy way
|
||||
to namespace events whilst in development and ensure interoperability, we have chosen not to use an unstable
|
||||
prefix for this change, on the understanding that in future we will be able to use the string `version` field
|
||||
for the unstable prefix.
|
||||
|
||||
For backwards compatibility, strongly typed implementations should allow for
|
||||
`version` to either be a string or the integer `0`.
|
||||
@ -0,0 +1,61 @@
|
||||
# MSC3860: Media Download Redirects
|
||||
|
||||
Currently the media download endpoints must return either a 200 with content or error responses. This
|
||||
means the media server instance must stream the data from wherever it is stored, which is likely not
|
||||
local to itself. Allowing redirects on these endpoints would make it possible for the media repo to
|
||||
tell clients/servers to pull data direct from the source, e.g. a CDN.
|
||||
|
||||
Additionally redirects could be used to avoid downloading media from untrusted homeservers. Currently
|
||||
users can make their homeserver download, cache and proxy any matrix mid that I want, including
|
||||
bad/illegal content. Allowing for a 307 redirect would permit cautious server operators to not
|
||||
store and provide any media that floats in the matrixverse, but just refer to the "original" media.
|
||||
|
||||
## Proposal
|
||||
|
||||
This MSC proposes that a 307 or 308 redirect code is allowed and followed according to the `Location`
|
||||
header. It is possible some clients would already follow these which needs to be confirmed. Specific
|
||||
endpoints in question ([current spec link for these](https://spec.matrix.org/v1.6/client-server-api/#get_matrixmediav3downloadservernamemediaid)):
|
||||
|
||||
+ `/_matrix/media/v3/download/{serverName}/{mediaId}`
|
||||
+ `/_matrix/media/v3/download/{serverName}/{mediaId}/{fileName}`
|
||||
+ `/_matrix/media/v3/thumbnail/{serverName}/{mediaId}`
|
||||
|
||||
To prevent breaking clients that don't properly follow the redirect response this functionality will
|
||||
be enabled by a query string flag `allow_redirect=true`. So specifically in the above cases if a
|
||||
client respects redirect responses it can make requests like so to the media endpoints:
|
||||
|
||||
+ `/_matrix/media/v3/download/{serverName}/{mediaId}?allow_redirect=true`
|
||||
+ `/_matrix/media/v3/download/{serverName}/{mediaId}/{fileName}?allow_redirect=true`
|
||||
+ `/_matrix/media/v3/thumbnail/{serverName}/{mediaId}?allow_redirect=true`
|
||||
|
||||
In the case where a client wishes not to redirect (either implicitly with no parameter or explicitly
|
||||
providing `allow_redirect=false`) the server must continue to serve media directly with no redirect.
|
||||
|
||||
## Potential Issues
|
||||
|
||||
None for clients, as opt-in functionality this change is 100% backwards compatible.
|
||||
|
||||
With redirects it becomes easier to force another homeserver to be your CDN, which isn't great
|
||||
(clients could already do that, but not without recompiling).
|
||||
|
||||
Redirects also potentially allow changing of media underneath the users as different redirects could
|
||||
be returned over time. It is worth noting that a badly behaving media server can already just return
|
||||
different content as well.
|
||||
|
||||
## Alternatives
|
||||
|
||||
None at this time.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
A media repo could redirect requests to a bad actor, although this would make the primary media
|
||||
repo itself a bad actor, thus this does not present any increased security issues.
|
||||
|
||||
## Unstable Prefix
|
||||
|
||||
Until this functionality has landed in the spec, the `allow_redirect` query
|
||||
parameter should be prefixed with `com.beeper.msc3860.`:
|
||||
|
||||
```
|
||||
?com.beeper.msc3860.allow_redirect=true
|
||||
```
|
||||
@ -0,0 +1,633 @@
|
||||
# MSC3952: Intentional Mentions
|
||||
|
||||
Mentioning other users on Matrix is difficult -- it is not possible to know if
|
||||
[mentioning a user by display name or Matrix ID](https://github.com/matrix-org/matrix-spec/issues/353)
|
||||
will count as a mention, but is also too easy to mistakenly mention a user.
|
||||
|
||||
(Note that throughout this proposal "mention" is considered equivalent to a "ping"
|
||||
or highlight notification.)
|
||||
|
||||
Some situations that result in unintentional mentions include:
|
||||
|
||||
* Replying to a message will re-issue pings from the initial message due to
|
||||
[fallback replies](https://spec.matrix.org/v1.5/client-server-api/#fallbacks-for-rich-replies).
|
||||
* A user without the power level to send `@room` can abuse this by including
|
||||
`@room` in a message and getting a user with the appropriate power levels
|
||||
to reply to them.
|
||||
* Each time a message is edited the new version will be re-evaluated for mentions.
|
||||
* Mentions occurring [in spoiler contents](https://github.com/matrix-org/matrix-spec/issues/16)
|
||||
or [code blocks](https://github.com/matrix-org/matrix-spec/issues/15) are
|
||||
evaluated.
|
||||
* If the [localpart of your Matrix ID is a common word](https://github.com/matrix-org/matrix-spec-proposals/issues/3011)
|
||||
then the push rule matching usernames (`.m.rule.contains_user_name`) matches
|
||||
too often (e.g. Travis CI matching if your Matrix ID is `@travis:example.org`).
|
||||
* If the [localpart or display name of your Matrix ID matches the hostname](https://github.com/matrix-org/matrix-spec-proposals/issues/2735)
|
||||
(e.g. `@example:example.org` receives notifications whenever `@foo:example.org`
|
||||
is replied to).
|
||||
|
||||
As a sender you do not know if including the user's display name or Matrix ID would
|
||||
even be interpreted as a mention (see [issue 353](https://github.com/matrix-org/matrix-spec/issues/353)).
|
||||
This results in some unexpected behavior and bugs:
|
||||
|
||||
* Matrix users use "leetspeak" when sending messages to avoid mentions (e.g.
|
||||
referring to M4tthew instead of Matthew).
|
||||
* It is impossible to ping one out of multiple people with the same localpart
|
||||
(or display name).
|
||||
* Since the relation between `body` and `formatted_body` is ill-defined and
|
||||
["pills" are converted to display names](https://github.com/matrix-org/matrix-spec/issues/714),
|
||||
this can result in missed messages. [^1]
|
||||
|
||||
There are also some other related bugs:
|
||||
|
||||
* Matrix users will append emoji or other unique text in their display names to
|
||||
avoid unintentional pings.
|
||||
* Bridging mentions is suboptimal since they [use display names](https://github.com/matrix-org/matrix-spec/issues/353#issuecomment-1055809364)
|
||||
as a workaround, e.g.:
|
||||
* It breaks the contract that bridges will not mutate the content of messages.
|
||||
* For some protocols, bridges need try to figure out if every message contains
|
||||
any of the possible nicknames of room members.
|
||||
* If a user changes their display name in a room,
|
||||
[they might not be mentioned unless the historical display name](https://github.com/matrix-org/matrix-spec/issues/353#issuecomment-1055809372)
|
||||
is used while processing push rules.
|
||||
|
||||
## Background
|
||||
|
||||
Mentions are powered by two of the default push rules that search an event's
|
||||
`content.body` property for the current user's display name
|
||||
([`.m.rule.contains_display_name`](https://spec.matrix.org/v1.5/client-server-api/#default-override-rules))
|
||||
or the localpart of their Matrix ID ([`.m.rule.contains_user_name`](https://spec.matrix.org/v1.5/client-server-api/#default-content-rules)).
|
||||
|
||||
There's also a [section about "user and room mentions"](https://spec.matrix.org/v1.5/client-server-api/#user-and-room-mentions)
|
||||
which defines that messages which mention the current user in the `formatted_body`
|
||||
of the message should be colored differently:
|
||||
|
||||
> 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.
|
||||
|
||||
## Proposal
|
||||
|
||||
The existing push rules for user and room mentions are deprecated and new rules,
|
||||
which use a property specific for mentions[^2], are added to make mentions simpler
|
||||
and more reliable for users.
|
||||
|
||||
### New event property
|
||||
|
||||
A new `m.mentions` property is added to the event content; it is an object with two
|
||||
optional properties:
|
||||
|
||||
* `user_ids`: an array of strings consisting of Matrix IDs to mention.
|
||||
* `room`: a boolean, true indicates an "@room" mention. Any other value or the
|
||||
property missing is interpreted as not an "@room" mention.
|
||||
|
||||
It is valid to include both the `user_ids` and `room` properties.
|
||||
|
||||
It is recommended that homeservers reject locally created events with an invalid
|
||||
`m.mentions` property with an error with a status code of `400` and an errcode of
|
||||
`M_INVALID_PARAM`.
|
||||
|
||||
Clients add a Matrix ID to the `user_ids` array whenever composing a message which
|
||||
includes an intentional mention, such as a ["pill"](https://spec.matrix.org/v1.5/client-server-api/#user-and-room-mentions).
|
||||
Clients set the `room` value to `true` when making a room-wide announcement. Clients
|
||||
should also set these values at other times when it is obvious the user intends to explicitly
|
||||
mention a user.[^3]
|
||||
|
||||
The `m.mentions` property is part of the plaintext event body and should be encrypted
|
||||
into the ciphertext for encrypted events.
|
||||
|
||||
### New push rules
|
||||
|
||||
Two new default push rule are added.
|
||||
|
||||
The `.m.rule.is_user_mention` override push rule would appear directly
|
||||
before the `.m.rule.contains_display_name` push rule:
|
||||
|
||||
```json
|
||||
{
|
||||
"rule_id": ".m.rule.is_user_mention",
|
||||
"default": true,
|
||||
"enabled": true,
|
||||
"conditions": [
|
||||
{
|
||||
"kind": "event_property_contains",
|
||||
"key": "content.m\\.mentions.user_ids",
|
||||
"value": "[the user's Matrix ID]"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
},
|
||||
{
|
||||
"set_tweak": "highlight"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
(Note: `\\.` would become a single logical backslash followed by a dot since the
|
||||
above is in JSON-representation. See
|
||||
[MSC3873](https://github.com/matrix-org/matrix-spec-proposals/pull/3873).)
|
||||
|
||||
The `.m.rule.is_room_mention` override push rule would appear directly
|
||||
before the `.m.rule.roomnotif` push rule:
|
||||
|
||||
```json
|
||||
{
|
||||
"rule_id": ".m.rule.is_room_mention",
|
||||
"default": true,
|
||||
"enabled": true,
|
||||
"conditions": [
|
||||
{
|
||||
"kind": "event_property_is",
|
||||
"key": "content.m\\.mentions.room",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"kind": "sender_notification_permission",
|
||||
"key": "room"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "highlight"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
An example event matching both `.m.rule.is_user_mention` (for `@alice:example.org`)
|
||||
and `.m.rule.is_room_mention` is provided below:
|
||||
|
||||
```json
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an example mention @alice:example.org",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example mention</b> <a href='https://matrix.to/#/@alice:example.org'>Alice</a>",
|
||||
"msgtype": "m.text",
|
||||
"m.mentions": {
|
||||
"user_ids": ["@alice:example.org"],
|
||||
"room": true
|
||||
}
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!somewhere:over.the.rainbow",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Client behavior
|
||||
|
||||
The overall user experience is not modified, beyond improving explicitness and
|
||||
reducing unintended mentions.
|
||||
|
||||
For example, it is common that a client will show an event with a mention in a
|
||||
different color (and denote the current user's "pill", as a way of showing the
|
||||
user *why* they were mentioned). This behavior is unchanged.
|
||||
|
||||
There are two variations that clients should take into account when decorating
|
||||
messages for mentions, however:
|
||||
|
||||
* The presence of a user's "pill" in a message no longer implies it is a mention.
|
||||
* This makes it easier to mention users without including their "pill" in a
|
||||
message (see [Abuse Potential](#abuse-potential) for ideas to combat this).
|
||||
|
||||
### Backwards compatibility
|
||||
|
||||
The [`.m.rule.contains_display_name`](https://spec.matrix.org/v1.5/client-server-api/#default-override-rules),
|
||||
[`.m.rule.contains_user_name`](https://spec.matrix.org/v1.5/client-server-api/#default-content-rules),
|
||||
and [`.m.rule.roomnotif`](https://spec.matrix.org/v1.5/client-server-api/#default-override-rules)
|
||||
push rules are to be deprecated.
|
||||
|
||||
To avoid unintentional mentions these rules are modified to only apply when the
|
||||
`m.mentions` property is missing; clients should provide at least an empty `m.mentions` property on
|
||||
every message to avoid the unintentional mentions discussed in the introduction.
|
||||
|
||||
A future room version may wish to disable the legacy push rules: clients would
|
||||
no longer be required to include the `m.mentions` property on every event. It
|
||||
maybe convenient to do this when extensible events are adopted (see
|
||||
[MSC3932](https://github.com/matrix-org/matrix-spec-proposals/pull/3932)).
|
||||
|
||||
After acceptance, it is likely for there to be disagreement about which push rules are
|
||||
implemented: legacy clients and homeservers may not yet have deprecated the
|
||||
`.m.rule.contains_display_name`, `.m.rule.contains_user_name`, and `.m.rule.roomnotif`
|
||||
push rules, while up-to-date clients and homeservers will support the
|
||||
`.m.rule.is_user_mention` and `.m.rule.is_room_mention` push rules. It is expected
|
||||
that both sets of push rules will need to be supported for a period of time, but
|
||||
at worst case should simply result in the current behavior (documented in the preamble).
|
||||
|
||||
If users wish to continue to be notified of messages containing their display name
|
||||
it is recommended that clients create a specific keyword rule for this, e.g. a
|
||||
`content` rule of the form:
|
||||
|
||||
```json
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
},
|
||||
{
|
||||
"set_tweak": "highlight"
|
||||
}
|
||||
],
|
||||
"pattern": "alice",
|
||||
"rule_id": "alice",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
### Impact on replies
|
||||
|
||||
Users are notified of replies via the `.m.rule.contains_display_name` or the
|
||||
`.m.rule.contains_user_name` push rule matching the
|
||||
[rich reply fallback](https://spec.matrix.org/v1.6/client-server-api/#fallbacks-for-rich-replies).
|
||||
Unfortunately these push rules will be disabled for events which contain the
|
||||
`m.mentions` property, i.e. all newly created events (see
|
||||
[above](#backwards-compatibility)). Clients should include the sender of the event
|
||||
being replied to as well as any mentioned users in that event (excluding yourself)
|
||||
in the new event's `m.mentions` property. The `room` property MUST NOT be copied over.
|
||||
|
||||
This signals that it is the *intention* of the sender to mention all of those people.
|
||||
This behavior may not make sense in all situations (e.g. an email-like client could
|
||||
provide both a "reply" and "reply all", while a microblogging client may wish to
|
||||
provide a "quote reply", dropping all mentions from the original event) and clients
|
||||
may wish to allow users to modify the list of mentioned users.
|
||||
|
||||
For example, if there is an event:
|
||||
|
||||
```json5
|
||||
{
|
||||
"sender": "@dan:example.org",
|
||||
"event_id": "$initial_event",
|
||||
"content": {
|
||||
"body": "Alice: Have you heard from Bob?",
|
||||
"m.mentions": {
|
||||
"user_ids": ["@alice:example.org", "@bob:example.org"]
|
||||
}
|
||||
},
|
||||
// other fields as required by events
|
||||
}
|
||||
```
|
||||
|
||||
And a reply from Alice:
|
||||
|
||||
```json5
|
||||
{
|
||||
"content": {
|
||||
"body": "> <@dan:example.org> Alice: Have you heard from Bob?\n\nNo, but I saw him with Charlie earlier.",
|
||||
"m.mentions": {
|
||||
"user_ids": [
|
||||
// Include the sender of $initial_event (optional).
|
||||
"@dan:example.org",
|
||||
// The users mentioned, minus yourself (optional).
|
||||
"@bob:example.org",
|
||||
// New mentions, as normal.
|
||||
"@charlie:example.org"
|
||||
]
|
||||
},
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$initial_event"
|
||||
}
|
||||
}
|
||||
},
|
||||
// other fields as required by events
|
||||
}
|
||||
```
|
||||
|
||||
If a user wishes to be notified of *all replies* to their messages, other solutions
|
||||
should be investigated, such as [MSC3664](https://github.com/matrix-org/matrix-spec-proposals/pull/3664).
|
||||
This would give more equal power to both senders and receivers of events.
|
||||
|
||||
### Impact on edits
|
||||
|
||||
Similarly to [replies](#impact-on-replies), users are notified of message edits
|
||||
via the `.m.rule.contains_display_name` or the `.m.rule.contains_user_name` push
|
||||
rule matching the [fallback content](https://spec.matrix.org/v1.6/client-server-api/#event-replacements).
|
||||
Generally this is undesirable and users do not need to be notified for the same
|
||||
message multiple times (e.g. if a user is fixing a typo).
|
||||
|
||||
Replacement events may have `m.mentions` properties in two locations:
|
||||
|
||||
* One at the top-level of the `content`, which should contain any users to mention
|
||||
*for this edit*.
|
||||
* One inside the `m.new_content` property, which should contain the full list of
|
||||
mentioned users in any version of the event, unless a mention is removed
|
||||
(see below).
|
||||
|
||||
It is recommended that clients use an empty top-level `m.mentions` property when
|
||||
editing an event, *unless* the edit is significant or if additional users are
|
||||
mentioned in the latest version.
|
||||
|
||||
For example, if there is an event:
|
||||
|
||||
```json5
|
||||
{
|
||||
"sender": "@dan:example.org",
|
||||
"event_id": "$initial_event",
|
||||
"content": {
|
||||
"body": "Hello Alice!",
|
||||
"m.mentions": {
|
||||
"user_ids": ["@alice:example.org"]
|
||||
}
|
||||
},
|
||||
// other fields as required by events
|
||||
}
|
||||
```
|
||||
|
||||
And an edit after realizing that Bob is also in the room:
|
||||
|
||||
```json5
|
||||
{
|
||||
"content": {
|
||||
"body": "* Hello Alice & Bob!",
|
||||
"m.mentions": {
|
||||
"user_ids": [
|
||||
// Include only the newly mentioned user.
|
||||
"@bob:example.org"
|
||||
]
|
||||
},
|
||||
"m.new_content": {
|
||||
"body": "Hello Alice & Bob!",
|
||||
"m.mentions": {
|
||||
"user_ids": [
|
||||
// Include all mentioned users.
|
||||
"@alice:example.org",
|
||||
"@bob:example.org"
|
||||
]
|
||||
},
|
||||
},
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.replace",
|
||||
"event_id": "$initial_event"
|
||||
}
|
||||
},
|
||||
// other fields as required by events
|
||||
}
|
||||
```
|
||||
|
||||
Mentions can also be removed as part of an edit. In this case, the top-level `m.mentions`
|
||||
property would not include the removed user IDs (you cannot cancel the notification from
|
||||
the previous event) or any previously notified users, and the removed user would also be
|
||||
removed from the `m.new_content` proprerty's copy of `m.mentions`.
|
||||
|
||||
This should limit duplicate, unnecessary notifications for users. If a user wishes
|
||||
to receive notifications for edits of events they were mentioned in then they
|
||||
could setup a push rule for the `content.m\\.new_content.m\\.mentions` property
|
||||
or potentially leverage [MSC3664](https://github.com/matrix-org/matrix-spec-proposals/pull/3664).
|
||||
|
||||
This implies that:
|
||||
|
||||
* If a client highlights a message visually (e.g. by coloring it red), then it
|
||||
should look at the `m.mentions` under `m.new_content` for edited messages.
|
||||
Otherwise, in the example above, Alice would not see the message as red, even
|
||||
though the intent was for her to be mentioned.
|
||||
* Any sort of processing of push rules, e.g. to display a notification (sound,
|
||||
toast, push notification), should occur without any special rules. I.e. the
|
||||
`.m.rule.is_user_mention` and `.m.rule.is_room_mention` should look at the
|
||||
`m.mentions` directly under `content` and not match for Alice.
|
||||
|
||||
### Impact on bridging
|
||||
|
||||
For protocols with a similar mechanism for listing mentioned users this should
|
||||
strengthen the bridging contract as it enables bridges to stop mutating the
|
||||
content of messages. The bridge should be able to map from the remote user ID
|
||||
to the bridged user ID and include that in the `m.mentions` property of the
|
||||
Matrix event & the proper field in the bridged protocol[^4].
|
||||
|
||||
For bridged protocols that do not have this mechanism, the bridge will only
|
||||
be able to stop mutating content on messages bridged *into* Matrix. Messages
|
||||
bridged out of Matrix will still need to embed the mention into the text
|
||||
content.[^5]
|
||||
|
||||
## Potential issues
|
||||
|
||||
### Abuse potential
|
||||
|
||||
This proposal makes it trivial to "hide" mentions since it does not require the
|
||||
mentioned Matrix IDs to be part of the displayed text. This is only a limitation
|
||||
for current clients: mentions could be exposed in the user interface directly.
|
||||
For example, a de-emphasized "notified" list could be shown on messages, similar
|
||||
to CCing users on an e-mail.
|
||||
|
||||
Although not including mentions in the displayed text could be used as an abuse
|
||||
vector, it does not enable additional malicious behavior than what is possible
|
||||
today. From discussions and research while writing this MSC there are moderation
|
||||
benefits to using a separate property for mentions:
|
||||
|
||||
* The number of mentions is trivially limited by moderation tooling, e.g. it may
|
||||
be appropriate for a community room to only allow 10 mentions. Events not abiding
|
||||
by this could be rejected automatically (or users could be banned automatically).
|
||||
* Various forms of "mention bombing" are no longer possible.
|
||||
* It is simpler to collect metrics on how mentions are being used (it is no longer
|
||||
necessary to process the textual `body` for every user's display name and local
|
||||
part).
|
||||
|
||||
Overall this proposal seems to be neutral or positive in the ability to combat
|
||||
malicious behavior.
|
||||
|
||||
### Encrypted mentions & `/notifications`
|
||||
|
||||
A previous version of this proposal (and the alternative [MSC1796](https://github.com/matrix-org/matrix-spec-proposals/pull/1796))
|
||||
suggested leaving the `m.mentions` property in cleartext. This was
|
||||
[deemed too large of a metadata leak](https://github.com/matrix-org/matrix-spec-proposals/pull/3952#discussion_r1112154200)
|
||||
and removed from this proposal (and MSC1796 was closed). A downside of this is
|
||||
that homeservers (still) will not be able to differentiate between notifications
|
||||
and mentions in many cases.
|
||||
|
||||
This mostly affects how often homeservers push to devices (see
|
||||
[MSC3996](https://github.com/matrix-org/matrix-spec-proposals/pull/3996)
|
||||
for more information), but also means that the `/notifications?only=highlight`
|
||||
API is not useful in encrypted rooms.
|
||||
|
||||
## Future extensions
|
||||
|
||||
### Combating abuse
|
||||
|
||||
Some ideas for combating abuse came from our discussion and research which are
|
||||
worth sharing. These ideas are not a requirement for implementing this MSC, and
|
||||
generally do not depend on it. (They could be implemented today with enough effort.)
|
||||
|
||||
It was recommended that clients could expose *why* an event has caused a notification
|
||||
and give users inline tools to combat abuse. For example, a client might show a tooltip:
|
||||
|
||||
> The sender of the message (`@alice:example.org`) mentioned you in this event.
|
||||
>
|
||||
> Block `@alice:example.org` from sending you messages? `[Yes]` `[No]`
|
||||
|
||||
Additionally, if a user sending a message is about to mention many people it can
|
||||
be useful to confirm whether they wish to do that (or prompt them to do an `@room`
|
||||
mention instead).
|
||||
|
||||
Moderators may find tooling to quickly find messages which mention many users
|
||||
useful in combating mention spammers. (Note that this should be made easier by
|
||||
this MSC.)
|
||||
|
||||
A future MSC might wish to explore features for trusted contacts or soft-ignores
|
||||
to give users more control over who can generate notifications.
|
||||
|
||||
### Muted except for mentions push rules
|
||||
|
||||
It might be desirable to have a "muted-except-for-mentions" feature for large (encrypted)
|
||||
rooms. This is particularly useful on iOS where a push notification can be decrypted
|
||||
via a background process but *cannot* be suppressed. This means it is not possible
|
||||
for the client to handle this feature and it must be handled on the server, unfortunately
|
||||
this would not be possible with the current proposal since the `m.mentions`
|
||||
property is encrypted (and the server cannot act on it).
|
||||
|
||||
Solving this problem is left to a future MSC, such as [MSC3996](https://github.com/matrix-org/matrix-spec-proposals/pull/3996)
|
||||
which builds on this proposal.
|
||||
|
||||
### Pillifying `@room`
|
||||
|
||||
Some clients attempt to create a "pill" out of `@room` mentions, but this is not
|
||||
a requirement of the Matrix specification. The current [user and rooms mentions](https://spec.matrix.org/v1.5/client-server-api/#user-and-room-mentions)
|
||||
section could be expanded for this situation.
|
||||
|
||||
### Extensible events
|
||||
|
||||
Handling of this property in [MSC1767](https://github.com/matrix-org/matrix-doc/pull/1767)-style
|
||||
extensible events is deliberately left for a future MSC to address, if needed.
|
||||
|
||||
### Role mentions
|
||||
|
||||
It is possible to add additional properties to the `m.mentions` object, e.g. a foreseeable
|
||||
usecase would be a `roles` property which could include values such as `admins` or
|
||||
`mods`. Defining this in detail is left to a future MSC.
|
||||
|
||||
### Cancelling notifications
|
||||
|
||||
It might be useful for a future MSC to investigate cancelling notifications if a
|
||||
user's mention is removed while [editing events](#impact-on-edits). This could
|
||||
be quite difficult as it is unclear if the mentioned user has already received
|
||||
the notification or not.
|
||||
|
||||
## Alternatives
|
||||
|
||||
### Prior proposals
|
||||
|
||||
There are a few prior proposals which tackle subsets of the above problem:
|
||||
|
||||
* [MSC1796](https://github.com/matrix-org/matrix-spec-proposals/pull/1796):
|
||||
similar to the proposal in this MSC, but limited to encrypted events (and kept
|
||||
in cleartext).
|
||||
* [MSC2463](https://github.com/matrix-org/matrix-spec-proposals/pull/2463):
|
||||
excludes searching inside a Matrix ID for localparts / display names of other
|
||||
users.
|
||||
* [MSC3517](https://github.com/matrix-org/matrix-spec-proposals/pull/3517):
|
||||
searches for Matrix IDs (instead of display name / localpart) and only searches
|
||||
specific event types & properties.
|
||||
* [Custom push rules](https://o.librepush.net/aux/matrix_reitools/pill_mention_rules.html)
|
||||
to search for matrix.to links instead of display name / localpart.
|
||||
|
||||
<details>
|
||||
|
||||
The above generates a new push rule to replace `.m.rule.contains_display_name`
|
||||
and `.m.rule.contains_user_name`:
|
||||
|
||||
```json
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"kind": "event_match",
|
||||
"key": "content.formatted_body",
|
||||
"pattern": "*https://matrix.to/#/@alice:example.org*"
|
||||
}
|
||||
],
|
||||
"actions" : [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
},
|
||||
{
|
||||
"set_tweak": "highlight"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
The last two proposals use a similar idea of attempting to find "pills" in the
|
||||
`formatted_body`, this has some downsides though:
|
||||
|
||||
* It doesn't allow for hidden mentions, which can be useful in some situations.
|
||||
* It does not work for event types other than `m.room.message`.
|
||||
|
||||
It also adds significant implementation complexity since the HTML messages must
|
||||
now be parsed for notifications. This is expensive and introduces potential
|
||||
security issues.
|
||||
|
||||
### Room version for backwards compatibility
|
||||
|
||||
Alternative backwards compatibility suggestions included using a new room version,
|
||||
similar to [MSC3932](https://github.com/matrix-org/matrix-spec-proposals/pull/3932)
|
||||
for extensible events. This does not seem like a good fit since room versions are
|
||||
not usually interested in non-state events. It would additionally require a stable
|
||||
room version before use, which would unnecessarily delay usage. Another MSC
|
||||
can address this concern, such as in the extensible events series, if
|
||||
desirable to be gated by a room version for a "clean slate" approach.
|
||||
|
||||
## Security considerations
|
||||
|
||||
None not already described.
|
||||
|
||||
## Unstable prefix
|
||||
|
||||
During development the following mapping will be used:
|
||||
|
||||
| What | Stable | Unstable |
|
||||
|---------------------|-------------------|--------------------------------------|
|
||||
| Event property | `m.mentions` | `org.matrix.msc3952.mentions` |
|
||||
| Push rule ID | `.m.rule.*` | `.org.matrix.msc3952.*` |
|
||||
|
||||
The server will include the `org.matrix.msc3952_intentional_mentions` flag in the
|
||||
`unstable_features` array of the `/versions` endpoint. If a client sees this flag
|
||||
it can choose to apply the deprecation logic discussed in the
|
||||
[backwards compatibility](#backwards-compatibility) section.
|
||||
|
||||
## Dependencies
|
||||
|
||||
This depends on the following (accepted) MSCs:
|
||||
|
||||
* [MSC3758](https://github.com/matrix-org/matrix-spec-proposals/pull/3758): Add `event_property_is` push rule condition kind
|
||||
* [MSC3873](https://github.com/matrix-org/matrix-spec-proposals/pull/3873): event_match dotted keys
|
||||
* [MSC3966](https://github.com/matrix-org/matrix-spec-proposals/pull/3966): `event_property_contains` push rule condition
|
||||
|
||||
<!-- Footnotes below: -->
|
||||
|
||||
[^1]: It is [defined as](https://spec.matrix.org/v1.5/client-server-api/#mroommessage-msgtypes)
|
||||
the the "plain text version of the HTML [`formatted_body`] should be provided in the `body`",
|
||||
but there is no particular algorithm to convert from HTML to plain text *except*
|
||||
when converting mentions, where the
|
||||
[plain text version uses the link anchor, not the link](https://spec.matrix.org/v1.5/client-server-api/#client-behaviour-26).
|
||||
|
||||
[^2]: As proposed in [issue 353](https://github.com/matrix-org/matrix-spec/issues/353).
|
||||
|
||||
[^3]: Note that this isn't really a change in behavior, it is just making the behavior
|
||||
explicit. It is expected that users already considered "pilled" users to be mentions,
|
||||
and it was more unexpected when non-pilled users *were* mentioned. This MSC fixes the
|
||||
latter case.
|
||||
|
||||
[^4]: Some protocols which provide structured data for mentions include
|
||||
[Twitter](https://developer.twitter.com/en/docs/twitter-api/data-dictionary/object-model/tweet),
|
||||
[Mastodon](https://docs.joinmastodon.org/entities/Status/#Mention),
|
||||
[Discord](https://discord.com/developers/docs/resources/channel#message-object),
|
||||
and [Microsoft Teams](https://learn.microsoft.com/en-us/graph/api/resources/chatmessagemention?view=graph-rest-1.0).
|
||||
|
||||
[^5]: Unfortunately some protocols do *not* provide structured data: the message
|
||||
itself must be parsed for mentions, e.g. IRC or
|
||||
[Slack](https://api.slack.com/reference/surfaces/formatting#mentioning-users).
|
||||
@ -0,0 +1,60 @@
|
||||
# MSC3987: Push actions clean-up
|
||||
|
||||
There are [two defined push rule actions](https://spec.matrix.org/v1.6/client-server-api/#actions)
|
||||
which are of dubious use:
|
||||
|
||||
* `coalesce` is defined, but unused in the spec and not implemented on any homeservers [^1]
|
||||
or clients [^2] beyond validating it or treating it identically to `notify`.
|
||||
* `dont_notify` is a no-op, but frequently used as a way to denote do nothing.
|
||||
It is clearer to provide an empty list of actions to avoid a situation where
|
||||
additional actions are given with it, e.g. `["notify", "dont_notify"]` is
|
||||
currently valid, but does not make sense. [^3]
|
||||
|
||||
## Proposal
|
||||
|
||||
The `coalesce` push rule action is removed from the Matrix specification.
|
||||
|
||||
The `dont_notify` push rule action is deprecated. Homeservers and clients should
|
||||
ignore it. Any [pre-defined rules](https://spec.matrix.org/v1.6/client-server-api/#actions)
|
||||
which include the `dont_notify` action are redefined to have an empty list of actions:
|
||||
|
||||
* `.m.rule.master`
|
||||
* `.m.rule.suppress_notices`
|
||||
* `.m.rule.member_event`
|
||||
|
||||
It is recommended that homeservers continue accepting the `coalesce` and `dont_notify`
|
||||
actions, but ignore them during processing. (Treating them as no-ops.) A future
|
||||
Matrix spec version should remove them completely.
|
||||
|
||||
## Potential issues
|
||||
|
||||
A client might attempt to create a push rule with a `coalesce` or `dont_notify`
|
||||
action that homeservers will reject as an unknown action.
|
||||
|
||||
## Alternatives
|
||||
|
||||
Do nothing and continue propagating confusion.
|
||||
|
||||
## Security considerations
|
||||
|
||||
None.
|
||||
|
||||
## Dependencies
|
||||
|
||||
None.
|
||||
|
||||
[^1]: [Dendrite](https://github.com/search?q=repo%3Amatrix-org%2Fdendrite+CoalesceAction+NOT+path%3A%2F_test.go%24%2F&type=code),
|
||||
[Synapse](https://github.com/search?q=repo%3Amatrix-org%2Fsynapse+coalesce+language%3ARust&type=code&l=Rust),
|
||||
[Conduit](https://gitlab.com/search?search=coalesce&nav_source=navbar&project_id=22083768&group_id=4616224&search_code=true&repository_ref=next),
|
||||
[Construct](https://github.com/matrix-construct/construct/blob/4ecf1ef037ecc1a5d1e3a1049d9a63cb0a6f3455/matrix/push.cc#L739-L740)..
|
||||
|
||||
[^2]: [matrix-js-sdk](https://github.com/search?q=repo%3Amatrix-org/matrix-js-sdk%20Coalesce&type=code),
|
||||
[matrix-ios-sdk](https://github.com/search?q=repo%3Amatrix-org%2Fmatrix-ios-sdk%20coalesce&type=code),
|
||||
[matrix-rust-sdk](https://github.com/matrix-org/matrix-rust-sdk/commit/59edc22a35c4ef162ea0a8cafccdf25e37ab1070),
|
||||
[matrix-android-sdk2](https://github.com/search?q=repo%3Amatrix-org/matrix-android-sdk2%20ACTION_COALESCE&type=code),
|
||||
[Ruma](https://github.com/search?q=repo%3Aruma/ruma%20Coalesce&type=code).
|
||||
|
||||
[^3]: It has been noted on recent MSCs that new rules should not use `dont_notify`,
|
||||
see [MSC3786](https://github.com/matrix-org/matrix-spec-proposals/pull/3786#discussion_r864607531),
|
||||
[MSC2153](https://github.com/matrix-org/matrix-spec-proposals/pull/2153#discussion_r450188777) /
|
||||
[MSC2677](https://github.com/matrix-org/matrix-spec-proposals/pull/2677#discussion_r879701007).
|
||||
Loading…
Reference in New Issue