Merge remote-tracking branch 'origin/main' into room-takeover

pull/3993/head
Christian Paul 3 years ago
commit ee199b2dff

@ -90,7 +90,7 @@ same major points is fine.
Some tips for MSC writing:
* Please wrap your lines to 80 characters maximum (some small leeway is OK).
* Please wrap your lines to 120 characters maximum.
This allows readers to review your markdown without needing to horizontally
scroll back and forth. Many markdown text editors have this a feature.
* If you are referencing an existing endpoint in the spec, or another MSC, it

@ -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,177 @@
# MSC3882: Allow an existing session to sign in a new session
In [MSC3906](https://github.com/matrix-org/matrix-spec-proposals/pull/3906) a proposal is made to allow a user to login
on a new device using an existing device by means of scanning a QR code.
In order to support the above proposal a mechanism is needed where by the new device can obtain a new access token that
it can use with the Client-Server API.
It is proposed that the current `m.login.token` mechanism is extended to allow the issuance of a login token by an
existing client session.
## Proposal
### New API endpoint POST /login/get_token
Add a new optional POST endpoint to the Client-Server API that issues a single-use, time-limited `m.login.token` token:
`POST /_matrix/client/v1/login/get_token`
The client should send an empty JSON object for the body of the `POST` request (apart from
the `auth` property used in user-interactive authentication).
As detailed in the security selection below, this new endpoint should be protected by user interactive authentication
(UIA) as detailed in the existing
["User-interactive API in the REST API"](https://spec.matrix.org/v1.5/client-server-api/#user-interactive-api-in-the-rest-api)
section of the spec.
Once UIA has been completed a `200` response with JSON body is returned. The body contains the following fields:
- `login_token` - required, the token to use with `m.login.token`
- `expires_in_ms` - required, how long until the token expires in milliseconds
An example response is as follows:
```http
HTTP/1.1 200 OK
Content-Type: application/json
```
```json
{
"login_token": "<login token>",
"expires_in_ms": 120000
}
```
This token can then be used as per the existing [Login spec](https://spec.matrix.org/v1.6/client-server-api/#login) as follows:
```http
POST /_matrix/client/v3/login HTTP/1.1
Content-Type: application/json
```
```json
{
"type": "m.login.token",
"token": "<login token>"
}
```
### Determining the availability of the new API endpoint
As this new API endpoint is optional, clients should determine whether the endpoint is available
before prompting the user to try using it.
There are two usage scenarios to consider:
1. The user wishes to sign in on a Matrix client.
2. The user wishes to use an already signed in Matrix client to sign in another client.
In scenario 2 the client is already authenticated. For scenario 1 the client is not yet authenticated.
#### Scenario 1: The user wishes to sign in on a Matrix client
The client wants to determine if it *may* be possible to sign in by getting a login token from an
existing session.
It is proposed that the unauthenticated client can determine if the new API endpoint *may* be available
as part of the existing
[`GET /_matrix/client/v3/login`](https://spec.matrix.org/v1.6/client-server-api/#get_matrixclientv3login)
API endpoint.
As the `m.login.token` mechanism is used to redeem the login token, the client can first determine if the
`m.login.token` is advertised as a flow in the `GET /_matrix/client/v3/login` response. Then it can check a
new boolean field `get_login_token` to determine if the capability *may* be available.
An example of the proposed `GET /_matrix/client/v3/login` response is:
```json
{
"flow": [
{
"type": "m.login.token",
"get_login_token": true
}
]
}
```
In this case the mechanism could be available and so the client could prompt the user to try using it.
#### Scenario 2: The user wishes to use an already signed in Matrix client to sign in another client
The client is already authenticated. The client can determine whether it is able and allowed to sign in
another client by checking the
[capabilities](https://spec.matrix.org/v1.6/client-server-api/#capabilities-negotiation)
advertised by the homeserver.
The unauthenticated client can also determine whether the new API endpoint is available
via the [capability negotiation](https://spec.matrix.org/v1.6/client-server-api/#capabilities-negotiation)
mechanism.
The homeserver can then decide on a per user basis if the capability is available or not. For example,
it could implement a policy based on some risk criteria around the users account, session, or device.
A new capability `m.get_login_token` is proposed. This capability has a single boolean flag, `enabled`, to
denote whether the `/login/get_token` API is available or not.
An example of the capability APIs response for this capability is:
```json
{
"capabilities": {
"m.get_login_token": {
"enabled": true
}
}
}
```
## Potential issues
None identified.
## Alternatives
If Matrix was already using OIDC as per [MSC3861](https://github.com/matrix-org/matrix-spec-proposals/pull/3861) then we
could use the device authorization grant flow which allows for a new device to be signed in using an existing device.
## Security considerations
A malicious client could use the mechanism to spawn more than one session. The following mitigations should be applied:
1. The homeserver must only allow the token to be used for a single login. If the user wishes to sign in multiple
additional clients a token must be issued for each client.
2. The homeserver should enforce
[user interactive authentication](https://spec.matrix.org/v1.6/client-server-api/#user-interactive-authentication-api)
by default for the new endpoint. The purpose being that consent is obtained from the user for each additional client.
3. The homeserver should enforce rate-limiting in accordance with the existing
[spec](https://spec.matrix.org/v1.6/client-server-api/#rate-limiting). It may be appropriate for the homeserver admin to
to configure a low limit ("low" relative to other enforced limits). For example, a rate of once per minute could be appropriate.
n.b. A homeserver admin may deem that they have suitable protections in place and offer the endpoint without UIA auth as described
in the existing spec:
> A request to an endpoint that uses User-Interactive Authentication never succeeds without auth. Homeservers may allow requests
> that dont require auth by offering a stage with only the m.login.dummy auth type, but they must still give a 401 response to
> requests with no auth data.
## Unstable prefix
While this feature is in development the following unstable prefixes should be used:
- API endpoint `/_matrix/client/v1/login/get_token` => `/_matrix/client/unstable/org.matrix.msc3882/login/get_token`
- capability `m.get_login_token` => `org.matrix.msc3882.get_login_token`
- login flow field `get_login_token` => `org.matrix.msc3882.get_login_token`
For reference - an earlier revision of this proposal used an unstable endpoint of
`/_matrix/client/unstable/org.matrix.msc3882/login/token` with an unstable feature advertised
in the response to `GET /_matrix/client/versions` as `org.matrix.msc3882`
set to `true`. This may be referred to as "revision zero" in existing implementations.
## Dependencies
None.

@ -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,227 @@
# MSC3970: Scope transaction IDs to devices
Transaction identifiers in the Client-Server API are currently scoped to the
concept of a "client session" which, when refresh tokens are used, can span a
sequence of access tokens.
The spec [reads](https://spec.matrix.org/v1.6/client-server-api/#transaction-identifiers):
> The scope of a transaction ID is a “client session”, where that session is
> identified by a particular access token. When refreshing an access token, the
> transaction IDs scope is retained. This means that if a client with token `A`
> uses `TXN1` as their transaction ID, refreshes the token to `B`, and uses
> `TXN1` again itll be assumed to be a duplicate request and ignored. If the
> client logs out and back in between the `A` and `B` tokens, `TXN1` could be used
> once for each.
The "client session" scope concept described can be complicated to implement.
This MSC proposes that the scope of a transaction identifier is changed to something
that is easier to implement whilst maintaining required transaction semantics.
The transaction IDs appear in two parts of the Client-Server API spec:
1. As a identifier to allow the homeserver to make some `PUT` endpoints
[idempotent](https://spec.matrix.org/v1.6/client-server-api/#transaction-identifiers)
2. An unsigned field in the event data model for a client to tell if it sent an
event or not. a.k.a. solving the
["local echo"](https://spec.matrix.org/v1.6/client-server-api/#local-echo) problem
For reference, the `PUT` endpoints that have the a `{txnId}` param are:
- [`PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}`](https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid)
- [`PUT /_matrix/client/v3/rooms/{roomId}/redact/{eventId}/{txnId}`](https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3roomsroomidredacteventidtxnid)
- [`PUT /_matrix/client/v3/sendToDevice/{eventType}/{txnId}`](https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3sendtodeviceeventtypetxnid)
## Proposal
It is proposed that the scope of transaction identifiers be changed from a
"client session" to a "device".
A "device" is typically represented by a `device_id` elsewhere in the spec.
For idempotency, this means the homeserver changing the method of identifying a
request from:
- (`client session`, `HTTP path of request which includes the transaction ID`)
to:
- (`device_id`, `HTTP path of request which includes the transaction ID`)
For local echo, the homeserver would now include the `transaction_id` in the
event data when it is serving a sync request from the same `device_id` as
determined from the access token.
## Potential issues
### This is technically a breaking change to the spec
The main "issue" I see with this proposal is that this is technically a breaking
change to the spec.
A device ID could have multiple sequences of access tokens associated
with it (since device ID can be specified as a parameter to
[`/login`](https://spec.matrix.org/v1.6/client-server-api/#post_matrixclientv3login)).
For example, a "bot" implementation might masquerade as the same "device" despite
calling `/login` on every run by passing the same device ID. Such a device might
also use the same transaction ID on each run.
Therefore it could potentially lead to a request being treated as a duplicate
where previously it would have been treated as a distinct request.
However, some evidence has been collated which suggests that nothing would be impacted
in reality:
#### 1. Data from the matrix.org homeserver suggests the change would have no impact
The `matrix.org` homeserver is a reasonable size deployment and could be considered
reasonably representative of the diversity of Matrix clients.
Synapse maintains a `event_txn_id` table
that contains a rolling 24 hour window of
(`user_id`, `token_id`, `room_id`, `txn_id`) tuples.
Having analysed the contents of the table, it appears that there are no repeated
transaction IDs for a given user, token and room.
n.b. not all `PUT` endpoints contribute to the table but I think the high-volume
ones do
This suggests that the widening of the scope from token to device would not have caused
any issues during the periods sampled.
For reference the following is the schema of the `event_txn_id` table:
```sql
Table "matrix.event_txn_id"
Column | Type | Collation | Nullable | Default
-------------+--------+-----------+----------+---------
event_id | text | | not null |
room_id | text | | not null |
user_id | text | | not null |
token_id | bigint | | not null |
txn_id | text | | not null |
inserted_ts | bigint | | not null |
Indexes:
"event_txn_id_token_id" btree (token_id)
"event_txn_id_event_id" UNIQUE, btree (event_id)
"event_txn_id_ts" btree (inserted_ts)
"event_txn_id_txn_id" UNIQUE, btree (room_id, user_id, token_id, txn_id)
Foreign-key constraints:
"event_txn_id_event_id_fkey" FOREIGN KEY (event_id) REFERENCES matrix.events(event_id) ON DELETE CASCADE
"event_txn_id_token_id_fkey" FOREIGN KEY (token_id) REFERENCES matrix.access_tokens(id) ON DELETE CASCADE
```
And the query to look for repeated transaction IDs:
```sql
SELECT e1.txn_id, LEFT(e1.user_id, 5) AS user_id, e1.token_id, e2.token_id, e1.inserted_ts, e2.inserted_ts FROM matrix.event_txn_id e1, matrix.event_txn_id e2 WHERE e1.txn_id = e2.txn_id AND e1.event_id <> e2.event_id AND e1.event_id < e2.event_id AND e1.user_id = e2.user_id AND e1.room_id = e2.room_id ORDER BY e1.token_id;
txn_id | user_id | token_id | token_id | inserted_ts | inserted_ts
--------+---------+----------+----------+-------------+-------------
(0 rows)
```
#### 2. Conduit homeserver already scopes transaction IDs to devices
As highlighted by the new Complement
[tests](https://github.com/matrix-org/complement/pull/613) the Conduit homeserver
is already scoping transaction IDs to devices.
I can't find a related issue [listed](https://gitlab.com/famedly/conduit/-/issues),
so presumably this non-compliant behaviour isn't causing a known issue for
admins and users of the Conduit homeserver?
### Is the "device" concept the right level of abstraction to use?
One way to look at it is that device is already widely used in the end-to-end
encryption parts of the spec and so why isn't it suitable for this use case too?
### What about two clients masquerading as a single device ID?
I don't know if this actually works in practice. If this was a concern then it
could be mitigated by clarifying in the spec that if a client wishes to submit
requests using the same `device_id` as another client session that it should
choose transaction identifiers that are unique to that client session.
## Alternatives
### Do nothing
We could leave the transaction ID scope as is.
However, it makes it difficult to implement a features like
[MSC3861: Matrix architecture change to delegate authentication via OIDC](https://github.com/matrix-org/matrix-spec-proposals/pull/3861)
as the concept of a "client session" doesn't really exist in OIDC.
As noted above, at least one homeserver implementation is also not implementing
the spec as it is today.
It also turns out that the current implementation of refresh tokens in Synapse
breaks the transaction ID semantics already and needs to be
[fixed](https://github.com/matrix-org/synapse/issues/15141).
### Make a backwards compatible change
A backwards compatible alternative could be something like:
1. For idempotency have clients opt-in to a new scope of transaction ID, but
support the current semantics too for compatibility
2. Have clients opt-in (e.g. request param on the sync endpoint) to receiving
transaction ID for all events in the sync response and make the client
responsible for identifying which messages they sent
The disadvantage of this is that we create a load of extra maintenance work to
support both semantics for a period of time for (empirically) no gain in return.
## Security considerations
A malicious client can adopt an existing device ID of a user. This could
possibly allow some kind of denial of service attack.
However, if such an attack where possible it would be possible to do so without
this MSC as device IDs are crucial to the implementation of end-to-end encryption.
## Other recommendations
I'm not suggesting that these recommendations are addressed in this proposal, but
more notes for future proposals or spec clarifications.
### Clarification on idempotency semantics
I have separately prepared a [spec PR](https://github.com/matrix-org/matrix-spec/pull/1449)
to clarify some of the idempotency semantics that doesn't modify the spec but is
useful to understand the context of this proposal.
### Clarification on transaction ID time scope
I also suggest that the spec be clarified over what time periods the transaction
ID is scoped for such that clients can be aware. This cloud simply be to say
that the time period is not defined and so may vary by implementation.
### Recommend a less naive transaction ID format
Currently the format of a transaction ID is not specified, but a recommendation
is [given](https://spec.matrix.org/v1.6/client-server-api/#transaction-identifiers):
> After the [previous] request has finished, the {txnId} value should be changed
> (how is not specified; **a monotonically increasing integer is recommended**).
I think this is an unnecessarily naive recommendation.
In most clients environments a pseudo-random number generator will be available and so
could be used to generate a UUID/ULID or similar.
As an aside, in my research I have found some clients use a "seconds since epoch" as a
transaction ID which introduces a limit on the maximum possible event transmission rate
per room to once per second. Perhaps a better recommendation could help prevent the
such behaviour being introduced in future.
## Unstable prefix
None needed.
## Dependencies
None.

@ -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…
Cancel
Save