-
Notifications
You must be signed in to change notification settings - Fork 417
MSC4325: Presence privacy #4325
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Signed-off-by: HarHarLinks <[email protected]>
Signed-off-by: Tobias Fella <[email protected]>
Signed-off-by: HarHarLinks <[email protected]>
Signed-off-by: HarHarLinks <[email protected]>
Signed-off-by: HarHarLinks <[email protected]>
Signed-off-by: HarHarLinks <[email protected]>
Signed-off-by: HarHarLinks <[email protected]>
Signed-off-by: HarHarLinks <[email protected]>
Signed-off-by: HarHarLinks <[email protected]>
Signed-off-by: HarHarLinks <[email protected]>
Signed-off-by: HarHarLinks <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implementation requirements:
- Client
- Server
| To do this, the server follows this algorithm: | ||
| 1. Send presence to all users matching a user ID explicitly listed, or matching a glob in, `allowed_users` | ||
| 2. Send presence to all users in a room with a room ID listed in `allowed_users` in membership state `join` (here, we do *not* allow globs, since there generally are no groups of rooms sharing useful patterns in their room ID), **unless** | ||
| 1. The user is [ignored](https://spec.matrix.org/latest/client-server-api/#ignoring-users) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this override apply for 1. at the very top (user is in allowed_users), too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This to applies also to the other comments concerning this algorithm: We probably shouldn't make this too complicated, and perhaps even more important if it can match a similar space in the spec then it should so we have neither to design nor implementers understand an entirely new system. Admittedly allowing room IDs has already removed us from that somewhat.
Originally this MSC left the logic open (for the time being) for that reason, and only introduced the 3 fairly generic "types" of list entries: MXID, MXID glob, room ID. Tobias added in the current logic to clarify that presence is opt-in. Then I thought of ignores... Now we are well on the slippery slope of exception rules. ;)
I reckon this is about the extent the API needs to fulfill its task, and the further we go the more convenience we get at the price of introducing a complicated model of rules. In this case, we are trading the maintenance of the allowed_users list and having more extensive globs, against another rule. What's better? Idk, maybe we are approaching this from the wrong side.
Let's consider the alternative of a fully UX-opinionated and more strongly server driven C2S API part. It might roughly look as follows:
{
"default": "<allow|deny>",
"invite": "<allow|deny>",
// further join rules? ...
"dms": "<allow|deny>",
"spaces": {
"allow": [ "!c01dc0ffee", ... ],
"deny": [ "!earlgrey", ... ]},
"rooms": {
"allow": [ "!deadbeef", ... ],
"deny": [ "!d09f00d", ... ]},
"homeservers": {
"allow": [ "matrix.org", ... ],
"deny": [ "evil.inc", ... ]},
"users": {
"allow": [ "@bob:example.org", ... ],
"deny": [ "@mallory:matrix.org", ... ]},
}(We could also split these into several events m.presence_sharing_config.defaults, m.presence_sharing_config.rooms, m.presence_sharing_config.homeservers, ... to avoid resending many long lists on updating one.)
We sprinkle a bit of logic on top:
Ignored users never receive presence. The fields (as displayed here) are ordered such that options listed further down override those higher up. The result is only returned after evaluating all rules.
- default: should be obvious that this is the fallback most likely to get overruled. if not set, deny.
- invite (and other join rules?): applies to rooms in general. ordered from most to least public, not sure if public is redundant with default.
- dms: being more intimate than
inviterooms, they get to override them, based on the assumption that it is more likely from a privacy perspective that a more intimate room is allowed over a less intimate one. being lower priority thanspacesallows users to group a number of DMs in a space e.g. "friends", "work", ... and override privacy there. this means they can also be overridden byrooms, however the preference to client UX should be to override byusersinstead. - spaces: evaluated to room membership recursively so long as the presence-sending user is a member of all rooms in the recursion chain (continuous hierarchy: Alice needs to be a member of !c01dc0ffee and !steamedmilk to see that !earlgrey is within !c01dcoffee via !steamedmilk).
If Charlie is a member of !c01dc0ffee, allow. If Charlie is a member of a subspace or room in !c01dc0ffee, allow. If !earlgrey is a subspace of !c01dc0ffee and Charlie is in a room in !earlgrey, deny (closest parent space). If the space hierarchy is cyclical, or the spaces do not contain each other, deny (default to privacy). - rooms: can override specific rooms incl dms. when applied to a space, then don't evaluate that space recursively.
- homeservers: considered only as part of a user ID, not literal servers.
@charlie:matrix.orgwould be allowed while@heinz:evil.incwould be denied. - users: highest specificity wins all.
When Alice's presence state is updated, it is up to the server to calculate all users in contact with her, then filter each user in the list through above rules, and finally federate to allowed_recipients. For example this could apply to Mallory: denied by default, but allowed by sharing an invite room, but denied by sharing a DM, denied by being a member of the !earlgrey hierarchy even though it is within !c01dc0ffee, but allowed because it is !deadbeef specifically, allowed anyway because Mallory uses matrix.org, but finally denied by a user-specific rule.
While I think this order of reasoning is simpler to understand for a human, implementations and spec should reverse this process to return early (special care needs to be taken for spaces).
A slight UI problem arises around the differentiation of DMs being displayed in a space based on space membership by some clients.
- âž– This allows a client not complete freedom in UI design or inventing new use cases.
- âž• Avoids client side glob parsing to display the settings nicely.
- âž– Adds server side logic that possibly needs to be re-evaluated whenever any room membership changes.
- âž• It makes it very easy on the client implementer to display the same abstracted settings on 2 devices, because the API is now fully abstract.
- 🟰 Does not match e.g. the invite filtering MSC4155: Invite filtering #4155 logic. (🟰 because tbh at this point we are rather far from that anyway.)
- âž• Removing globs makes the required rules much simpler, avoids pitfalls and possible unintended side effects
- ?
...honestly, sounds in favor of the opinionated approach to me.
| 2. The receiving user is listed in `denied_users` explicitly or by matching a glob | ||
| 3. The room's ID is listed under `denied_users` | ||
|
|
||
| This makes it explicit that a server MUST NOT send presence to any user that the sending user does share a room with or lists in `allowed_users`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the negation off here? Should the server not ONLY send presence to such users?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds to me like that was the intention. @TobiasFella?
| This makes it explicit that a server MUST NOT send presence to any user that the sending user does share a room with or lists in `allowed_users`. | |
| This makes it explicit that a server MUST NOT send presence to any user that the sending user does not share a room with or lists in `allowed_users`. |
| However this would introduce the minor risk of discrepancy that the receiving server sees a different member | ||
| list than the sending server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the worst that could happen here that a user joins the room in between the EDU being sent and received? If so this seems somewhat negligible since the new user would be included (or not) in the next update anyway?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Iirc we were thinking of partial state, room splits, net splits, malicious homeservers, etc. However right now I can't remember any convincing argument that holds up against the security considerations section.
| - A default value (allow or deny) | ||
| - A list of MXIDs to be treated as exceptions to the default | ||
| - A list of servers to be treated as exceptions to the default | ||
| - Allowing to share presence with all users the user shares a DM with | ||
| - Per-room settings to share presence with everyone in this room |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some example configurations might be helpful to understand the scheme below.
For instance, IIUC, a block list would use empty allowed_users (because * would short-circuit on point 1. and prevent exclusions)? An allow list, on the other hand, would use * in denied_users?
| 1. Send presence to all users matching a user ID explicitly listed, or matching a glob in, `allowed_users` | ||
| 2. Send presence to all users in a room with a room ID listed in `allowed_users` in membership state `join` (here, we do *not* allow globs, since there generally are no groups of rooms sharing useful patterns in their room ID), **unless** | ||
| 1. The user is [ignored](https://spec.matrix.org/latest/client-server-api/#ignoring-users) | ||
| 2. The receiving user is listed in `denied_users` explicitly or by matching a glob |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this exclude globs as otherwise denied_users = ["*"] would make it impossible to add exceptions for rooms?
| some of these rooms are listed under `denied_users`, the user still receives presence, as long as they share at | ||
| least one room which is not listed in `denied_users`. If *all* shared rooms are listed under `denied_users`, the user does not receive presence. | ||
|
|
||
| Clients SHOULD update the list of room IDs in the exception list when following room upgrades. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it also be advisable for clients to let users choose their presence sharing configuration before joining a room? I guess the join itself exposes presence-like information, too, so not sure if it's really relevant or not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This API design is fairly removed from representing specific UX, because there are just too many options.
my assumption is somewhere around
- homeserver implementations set reasonable defaults (could be to require the admin to set a default)
- homeserver admins can set reasonable defaults. for example my private $community homeserver might set
*:$localhoston the allowlist, but a public hs like tchncs.de would not. - homeserver admins can set overrides e.g. even when you as user allow
*:evil.inc, as admin I can refuse to send presence there. whether as a user trying to set it should result in an error is unclear yet? - clients set reasonable defaults (they already need to for the existing "send presence from this device" flag), ideally they have reasonable login UX to remind the user of the "send presence from this device" feature.
- clients could add a setup step if they offer an account onboarding of some kind.
- clients relate their UX for "presence on this device" and "whom to share presence with" - they point out to the users when they don't match, for example if the allowlist has
*but client presence is off - now, clients are free in how they want to let users configure their "presence privacy". some examples
- default allow/deny plus a list of exceptions (this might also be UI)
- DMs on/off
- private rooms on/off
- public rooms on/off
- list of servers
- list of users
- list of rooms
- list of spaces (infer child rooms clientside!)
- room settings of a DM -> user ID
- room settings of a room -> room ID
- when a room currently denied gets added to a space currently allowed show an info about that, asking to update
So about your proposal: it would be feasible for a client to show a presence option in, say, the room join preview screen. From a product perspective I can imagine this not being used too much; in fact I think the defaults for DMs/private/public rooms and server lists plus perhaps the combination with ignores will be most effective, and that would already be quite a detailed setup.
Signed-off-by: HarHarLinks <[email protected]>
| A possible solution would be for servers to federate presence to servers with many recipients less frequently or based | ||
| on additional heuristics (e.g. prioritise DMs, invite-only rooms, etc). | ||
|
|
||
| TODO: event size limit EDU? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TobiasFella wdyt? 256 MXIDs of up to 255 bytes each means O(64k).
| TODO: event size limit EDU? | |
| Though the spec does not currently limit the size of EDUs, this proposal can lead to extremely long lists of `allowed_recipients` being sent over federation, e.g. when presence is shared with big rooms. | |
| We recommend to chunk the presence EDUs to chunks of 256 MXIDs per presence EDU. | |
| This is trivial to implement: any remaining MXIDs can be transmitted in a second EDU. |
|
See also matrix-org/matrix-spec#428 |
Rendered
Co-written with @TobiasFella at Matrix Community Summit 2025.
Signed-off-by: HarHarLinks [email protected]
In line with matrix-org/matrix-spec#1700, the following disclosure applies:
I am employed at Nordeck, Matrix community member, and Member of the Matrix.org Foundation Governing Board. This proposal was written and published a community member.