Skip to content
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

MSC3189: Per-room/per-space profiles #3189

Draft
wants to merge 7 commits into
base: old_master
Choose a base branch
from
128 changes: 128 additions & 0 deletions proposals/3189-per-room-per-space-profile-data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# MSC3189: Per-room/per-space profile data
robintown marked this conversation as resolved.
Show resolved Hide resolved

People frequently have different identities in different communities. In the
context of Matrix, users may therefore want their display name or avatar to
appear differently in certain social contexts, such as within a room or a space.

While most clients technically already support per-room display names (by
getting profile data from a user's membership events in a room), this feature
suffers from a lack of documentation and server-side support. This proposal
attempts to improve on per-room/per-space profile data in the following ways:

1. Documenting the current de-facto mechanism for per-room profile data
2. Keeping global profile changes from overwriting per-room profile data (if desired)
3. Allowing clients to set profile data for an entire space in a single request rather than sending membership events in bulk

## Proposal

### Per-room profile data

First, the existing behavior: When showing a user's display name or avatar in a
room, clients should reference the `displayname` and `avatar_url` attributes of
the user's `m.room.member` state. Thus, to set a display name or avatar in a
specific room, clients should modify these attributes via the relevant state
APIs.

In order to prevent per-room profile data from being overwritten when the user
updates their global profile, an optional query parameter named `force` of type
`boolean` is added to the `PUT /_matrix/client/r0/profile/{userId}/avatar_url`
and `PUT /_matrix/client/r0/profile/{userId}/displayname` endpoints.

If `force` is `true`, the profile change is propagated to all of the user's
Copy link
Member

@ara4n ara4n May 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this further, I realised that we may be doubling down on what's effectively a major design flaw in Matrix today: that setting a profile scales O(N) with the number of rooms you are in. For instance, I just saw someone take 3 minutes to change their displayname because they were in a few hundred rooms :( - see also https://github.com/matrix-org/matrix-doc/issues/3194 and matrix-org/synapse#1297

I wonder if we should fix this first (and then make it support per-room/space semantics at the same time) rather than further entrenching the current misdesign. This could also be interesting in terms of simplifying lazy loading, as currently the main reason to LL membership events at all is so the timeline has the profile details to render their messages correctly.

One slightly crazy solution could be to switch to portable identities (a la MSC #2787 or MSC #1228), where each user has a different user ID in every room. Then, each per-room user ID could legitimately publish on their extensible profile (MSC #1769) a pointer to their displayname/avatar, and we never have to set these per room at all.

A simpler idea (which doesn't improve LL, and is more aligned with this specific MSC) would be to have the membership events reference a profile room for the profile data rather than a literal displayname/avatar. You could point it to whatever persona's profile room you like, and then by changing the profile there, it'd propagate everywhere. You could avoid impersonation by having the personae profile room say which mxid the personae is intended for. For instance, my m.room.member event could have an m.profile: !ara4npr0fil3:matrix.org, and that room could have a state event of m.displayname: ara4n. This then gives us per-space profiles fairly easily, as it's just up to the client to select the right personae when participating in a space. However, it depends entirely on MSC #1769 and peeking (#2753 and #2444) working efficiently and nicely so that clients can calculate the right profile. Also, the act of switching persona (as opposed to renaming a persona) would still be O(N) with number of rooms affected.

I don't particularly want to block this MSC on #1769 (let alone #2787), but wanted to raise this concern for completeness. If #1769 ends up ready sooner than later, I think be better to skip forward to proper personae extensible profiles rather than bodging the O(N) behaviour further.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think doing this based on profile rooms/identities with #1769 would be very nice. As someone who's very affected by the problem you mentioned earlier (~1300 rooms, changing avatars/display names is tremendously painful). Entrenching the current misdesign (as you put it) would be something I wouldn't be very comfortable with.

A sidenote on that though: Currently we have a specific avatar and display name for each message: When we link a profile room instead, we do loose that information, because it's not in the state for that specific information anymore, right? I don't think that's a bad thing, just something we need to consider.

Last but not least: would such a change require a room version bump? The format of the m.room.member event changing doesn't really sound like a good idea to me without a room version bump, if only for the reason that we don't mix-and-match the two different variant inside one room.

Copy link
Member

@ara4n ara4n May 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Historical profile values could be achieved by specifying timestamp ranges in the extensible profile room - ie "this persona was called Bob from 2003-2006 and then B0b from 2006-2007".

Not sure this would technically need a new room version, as it doesn't change federation and it could still be backwards compatible - but you'd need a way to tell clients that the new profiles are in use.

For this to work nicely in practice we'd need to right ensure v3 sync can lazyload the profile data without the client having to explicitly peek into all the relevant profile rooms.

(also, portable accounts are completely orthogonal to this problem, in retrospect)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While reading this a thought came to mind, you could sort of half way house this, by allowing profile info in rooms to reference another room. The obvious use of this is the profiles as rooms idea discussed here, but alternatively you could instead reference the space room and just inherit your profile info in the m.room.member event in there?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Cadair That was the thought I was playing with initially for how to improve this MSC. Though I quite liked it and was rewriting this MSC around that idea, it would preserve the O(n)-ness which I agree is a major flaw. Given that this is probably the right time to fix it being O(n), but #1769 is not quite ready, I'm not exactly sure where to go from here, as this pretty much comes down to priorities… The prospect of having native support for a #1769 solution in v3 /sync is encouraging though.

On the other hand, migrating from a system of manual propagation to a persona-room-based solution at a later date could be simple, I think? As long as both have a concept of profile scope and inheritance (which both would as I currently envision them) and the CS endpoints would look the same, I believe this would make persona rooms a natural upgrade path for when we do want to add extensible profile data and O(1) updates. @ara4n Thoughts on this course of action?

rooms by adding, updating, or removing the relevant attribute of the user's
`m.room.member` state (and only that attribute) as needed. Unlike the current
behavior, updating `displayname` *must not* cause the user's `avatar_url` to
change in any rooms, and vice versa.

If `force` is `false` (the default value), the profile change is only propagated
to rooms in which the relevant attribute (`displayname` or `avatar_url`) is
equal to that of the user's global profile before the update. This ensures that
by default, custom per-room profile data will not be overwritten.
robintown marked this conversation as resolved.
Show resolved Hide resolved

### Per-space profile data

Per-space profile data is communicated in the same way as global and per-room
profile data, by updating the relevant `m.room.member` attributes in the
space-room and all of its children, recursively. To make this a simple operation
for clients, another optional query parameter named `space` of type `string` is
added to the `PUT /_matrix/client/r0/profile/{userId}/avatar_url` and `PUT
/_matrix/client/r0/profile/{userId}/displayname` endpoints.

If specified, `space` must be a valid ID of a room of which the user is a member
(regardless of whether it is of type `m.space`<sup id="a1">[1](#f1)</sup>), and
its effect is to limit the scope of the profile change to the given space. This
is achieved by first updating the per-room profile data for the given
space-room, and then recursing into all `m.space.child` rooms of which the user
is a member.

The `space` parameter obeys `?force=false` as well, by only overwriting an
`m.room.member` attribute if it matches the previous profile data of the root
space. If this is not the case, meaning a room with a different per-room profile
has been found, the profile change stops there and does not continue recursing
into the room's space children. Additionally, servers must take care to handle
cycles in the space graph and not recurse infinitely (e.g. by tracking which
rooms it has visited).

## Potential issues
robintown marked this conversation as resolved.
Show resolved Hide resolved

This proposal assumes that having "one true display name per room" is a
desirable feature, since it minimizes complexity for clients and is compatible
with how most implementations already determine profile data. However, since
rooms can belong to multiple spaces, possibly with conflicting profile data,
this causes a certain degree of arbitrariness in what profile data gets set for
such rooms (depending purely on the order in which the user sets their per-space
profiles, and whether `force` is set). If this matters, users can always drill
down to room-level profile settings, though, and clients may assist them e.g. by
displaying a list of applicable per-space profiles to switch between.

Arguably, per-space profile data should be a more first-class feature, with
server support for things like inheriting profile data from parent spaces on
join. This proposal leaves it up to clients to implement such "inheritance"
behavior as they see fit, by altering individual `m.room.member` states when
rooms are joined, added to spaces, etc. If desired, servers could be changed to
automate some of this behavior in the future, though arguably this should be
left to clients, since they have more context for e.g. which parent space the
user was viewing a room from when they joined it.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving it to clients makes sense when joining initially, but when updating my name/avatar it is left to the sever. My understanding is that this is to save the client from needing to send hundreds or thousands of m.room.member events. It seems that since the display name and avatar that you use in a room transcends clients it probably makes sense to have server support here to ensure that consistent policies can be applied.

To be honest the multi-space room issue makes me uneasy. I would expect that setting my avatar in two spaces would have the same result irrespective of which order I performed the updates. I think that anything else is an unfortunate behaviour and we should try really hard to avoid it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback, now that I've thought it over I think I do actually want to handle these concerns in a more explicit manner.

I still believe that having one actual profile per room as part of m.room.member state is the way to go, given this "just works" for clients that don't care about scoped profiles, and because users could be very confused if it looks like different people are talking depending on which parent space they view a room from.

However servers could certainly do more to acknowledge conflicts when they exist, by keeping explicit track of what overrides exist and what their scopes are, as you suggest. This would also very likely be necessary for proper application of inheritance policies, which I'll try to integrate into this proposal as it does seem pretty important for UX.

Will mark this as a draft until I can make the proper revisions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I agree that putting the profile in the room makes sense. I don't think it is the only option but I think it is quite viable.

I think the key is fixing the "update" procedure. I can imagine some simple solutions such as the user has a list of profiles and where they apply. The order of the list can indicate priority and resolve conflicts. This order can potentially be generated explicitly but there is the option to adjust it when necessary. Fancy clients could warn you when setting a per-space avatar will update a room using a non-default avatar and ask you to resolve. This is a quick sketch but I think there are a number of options.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The update procedure should now be more in line with what you're asking for, and I've added server support for inheriting on joins. This piece of feedback still stands:

I would expect that setting my avatar in two spaces would have the same result irrespective of which order I performed the updates.

…though I'm personally not sure how much value there is in solving it


Finally, there is a pre-existing issue with the profile APIs: If the server has
to propagate a profile change to a large number of rooms, it could take a long
time, and the client could easily time out, potentially leaving some rooms
without the updated profile data. This is nothing new, but this proposal would
make it an even more broken state, since future attempts at changing profile
data without `?force=true` would interpret the rooms that weren't updated as all
having custom per-room profiles. It is expected that a future MSC will address
this by making the profile APIs non-blocking.

## Alternatives

An alternative would be to store per-room/per-space profile data as a part of
[extensible profiles](https://github.com/matrix-org/matrix-doc/pull/1769),
essentially keeping a public mapping of room IDs → profile data in a single
location. While altering `m.room.member` state gives us per-room and per-space
profile data for free, this alternative would require more action from clients
to implement. It would also leak data about users' profiles in private rooms,
which is a significant privacy concern, and it is unclear how conflicting
profiles would affect the "one source of truth" given by `m.room.member` state.
Furthermore, extensible profiles seem unlikely to land anytime soon, while
robintown marked this conversation as resolved.
Show resolved Hide resolved
per-room/per-space profile data is arguably a more urgent feature, and should
not depend on it.

## Security considerations

None that I am aware of.

## Unstable prefix

During development of this feature the versions of the profile APIs augmented
with `force` and `space` will be available at unstable endpoints:

```text
PUT /_matrix/client/unstable/org.matrix.msc3189/profile/{userId}/avatar_url
PUT /_matrix/client/unstable/org.matrix.msc3189/profile/{userId}/displayname
```

## Footnotes

<a id="f1"/>[1]: Room type is ignored primarily to be consistent with how the
space summary API handles room IDs, and also since rooms can technically have
`m.space.child` rooms without being of type `m.space` themselves. [↩](#a1)