Merge 7142bc3e02
into d6edcbd946
commit
fe276ac83c
@ -0,0 +1,148 @@
|
||||
# MSC4115: membership metadata on events
|
||||
|
||||
## Background
|
||||
|
||||
Consider the following Event DAG:
|
||||
|
||||
```mermaid
|
||||
graph BT;
|
||||
B[Bob joins];
|
||||
B-->A;
|
||||
C-->A;
|
||||
D-->B;
|
||||
D-->C;
|
||||
```
|
||||
|
||||
Bob has joined a room, but at the same time, another user has sent a message
|
||||
`C`.
|
||||
|
||||
Depending on the configuration of the room, Bob's server may serve the event
|
||||
`C` to Bob's client. However, if the room is encrypted, Bob will not be on the
|
||||
recipient list for `C` and the sender will not share the message key with Bob,
|
||||
even though, in an absolute time reference, `C` may have been sent at a later
|
||||
timestamp than Bob's join.
|
||||
|
||||
Unfortunately, there is no way for Bob's client to reliably distinguish events
|
||||
such as `A` and `C` that were sent "before" he joined (and he should therefore
|
||||
not expect to decrypt) from those such as `D` that were sent later. This
|
||||
situation is discussed in more detail at
|
||||
https://github.com/element-hq/element-meta/issues/2268.
|
||||
|
||||
A similar scenario can arise even in the absence of a forked DAG: clients
|
||||
see events sent when the user was not in the room if the room has [History
|
||||
Visibility](https://spec.matrix.org/v1.10/client-server-api/#room-history-visibility)
|
||||
set to `shared`. (This is fairly common even in encrypted rooms, partly because
|
||||
that is the default state for new rooms even using the `private_chat` preset
|
||||
for the [`/createRoom`](https://spec.matrix.org/v1.10/client-server-api/#post_matrixclientv3createroom)
|
||||
request, and also because history-sharing solutions such as
|
||||
[MSC3061](https://github.com/matrix-org/matrix-spec-proposals/pull/3061) rely
|
||||
on it.
|
||||
|
||||
As a partial solution to the forked-DAG problem, which will also solve the
|
||||
problem of historical message visibility, we propose a mechanism for servers to
|
||||
inform clients of their room membership at each event.
|
||||
|
||||
## Proposal
|
||||
|
||||
The `unsigned` structure contains data added to an event by a homeserver when
|
||||
serving an event over the client-server API. (See
|
||||
[specification](https://spec.matrix.org/v1.9/client-server-api/#definition-clientevent)).
|
||||
|
||||
We propose adding a new optional property, `membership`. If returned by the
|
||||
server, it should contain the membership of the user making the request,
|
||||
according to the state of the room at the time of the event being returned. If
|
||||
the user had no membership at that point (ie, they had yet to join or be
|
||||
invited), `membership` is set to `leave`. Any changes caused by the event
|
||||
itself (ie, if the event itself is a `m.room.member` event for the requesting
|
||||
user) are *included*.
|
||||
|
||||
In other words: servers should follow the following algorithm when populating
|
||||
the `unsigned.membership` property on an event E and serving it to a user Alice:
|
||||
|
||||
1. Consider the room state just *after* event E landed (accounting for E
|
||||
itself, but not any other events in the DAG which are not ancestors of E).
|
||||
2. Within the state, find the event M with type `m.room.member` and `state_key`
|
||||
set to Alice's user ID.
|
||||
3. * If no such event exists, set `membership` to `leave`.
|
||||
* Otherwise, set `membership` to the value of the `membership` property of
|
||||
the content of M.
|
||||
|
||||
It is recommended that homeservers populate the new property wherever
|
||||
practical, but they may omit it if necessary (for example, if calculating the
|
||||
value is expensive, servers might choose to only implement it in encrypted
|
||||
rooms). Clients must in any case treat the new property as optional.
|
||||
|
||||
For the avoidance of doubt, the new `membership` property is added to all
|
||||
Client-Server API endpoints that return events, including
|
||||
[`/sync`](https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3sync),
|
||||
[`/messages`](https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3roomsroomidmessages),
|
||||
[`/state`](https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3roomsroomidstate),
|
||||
and deprecated endpoints such as
|
||||
[`/events`](https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3events)
|
||||
and
|
||||
[`/initialSync`](https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3events).
|
||||
|
||||
|
||||
Example event including the new property:
|
||||
|
||||
```json
|
||||
{
|
||||
"content": {
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$26RqwJMLw-yds1GAH_QxjHRC1Da9oasK0e5VLnck_45",
|
||||
"origin_server_ts": 1632489532305,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@user:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1567437,
|
||||
"membership": "leave",
|
||||
"redacted_because": {
|
||||
"content": {
|
||||
"reason": "spam"
|
||||
},
|
||||
"event_id": "$Nhl3rsgHMjk-DjMJANawr9HHAhLg4GcoTYrSiYYGqEE",
|
||||
"origin_server_ts": 1632491098485,
|
||||
"redacts": "$26RqwJMLw-yds1GAH_QxjHRC1Da9oasK0e5VLnck_45",
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@moderator:example.org",
|
||||
"type": "m.room.redaction",
|
||||
"unsigned": {
|
||||
"membership": "leave",
|
||||
"age": 1257
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Potential issues
|
||||
|
||||
* Depending on server implementation, it may be expensive or difficult to
|
||||
implement? Feedback welcome from HS authors on this point.
|
||||
|
||||
## Alternatives
|
||||
|
||||
1. https://github.com/element-hq/element-meta/issues/2268#issuecomment-1904069895
|
||||
proposes use of a Bloom filter — or possibly several Bloom filters — to
|
||||
mitigate this problem in a more general way. It is the opinion of the author of
|
||||
this MSC that there is room for both approaches.
|
||||
|
||||
2. We could attempt to calculate the membership state on the client side. This
|
||||
might help in a majority of cases, but it will be unreliable in the presence
|
||||
of forked DAGs, state resolution, etc.
|
||||
|
||||
## Security considerations
|
||||
|
||||
None foreseen.
|
||||
|
||||
## Unstable prefix
|
||||
|
||||
While this proposal is in development, the name `io.element.msc4115.membership`
|
||||
should be used in place of `membership`.
|
||||
|
||||
## Dependencies
|
||||
|
||||
None.
|
Loading…
Reference in New Issue