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

Conversation

robintown
Copy link
Member

@robintown robintown commented May 11, 2021

@robintown robintown changed the title MSC3187: Per-room/per-space profile data MSC3189: Per-room/per-space profile data May 11, 2021
@robintown robintown force-pushed the room-space-profiles branch from b2f959e to 4fd6f1a Compare May 11, 2021 16:42
@turt2live turt2live added client-server Client-Server API kind:feature MSC for not-core and not-maintenance stuff proposal A matrix spec change proposal proposal-in-review labels May 11, 2021
@robintown
Copy link
Member Author

First MSC, eager to see how this goes! :)

Copy link
Contributor

@ShadowJonathan ShadowJonathan left a comment

Choose a reason for hiding this comment

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

Some behaviour clarification comments, otherwise this LGTM for direct PoC

proposals/3189-per-room-per-space-profile-data.md Outdated Show resolved Hide resolved
proposals/3189-per-room-per-space-profile-data.md Outdated Show resolved Hide resolved
proposals/3189-per-room-per-space-profile-data.md Outdated Show resolved Hide resolved
Signed-off-by: Robin Townsend <[email protected]>
proposals/3189-per-room-per-space-profile-data.md Outdated Show resolved Hide resolved
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

@robintown robintown marked this pull request as draft May 13, 2021 04:12
`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?

@daenney
Copy link

daenney commented May 17, 2021

One thing I'm curious about is addressing how to set your profile data before joining a room. Based on the client behaviour I've seen so far, you can only change it after you've joined a room, and I believe this proposal doesn't alter that. If I understand how it interacts with Spaces correctly, it would mostly "automate" altering the m.room.member state but the behaviour of that needing to happen after a join stays intact?

A consequence of that is that my global nickname and avatar leak into every room I join and can be observed before it gets altered. This limits the usefulness of per room/space profile data a lot when it comes to separating identities, especially as it relates to the avatar image.

@robintown
Copy link
Member Author

@daenney

If I understand how it interacts with Spaces correctly, it would mostly "automate" altering the m.room.member state but the behaviour of that needing to happen after a join stays intact?

I've actually decided that I need to rework this MSC to also automate the inheritance of profile data on joins and other actions (hence the draft label), so this should no longer be an issue.

A consequence of that is that my global nickname and avatar leak into every room I join and can be observed before it gets altered. This limits the usefulness of per room/space profile data a lot when it comes to separating identities, especially as it relates to the avatar image.

Presumably your global profile is something that is known and intended to be public, since anyone can look it up if they know your MXID. Though when I rework this MSC, I will definitely make sure to carefully consider privacy issues that could stem from inheriting per-space profiles without any action from the user.

@kevincox
Copy link
Contributor

One thing I'm curious about is addressing how to set your profile data before joining a room

IIUC this is a client issue. The client is the one that sends your profile. It is just that currently all clients send your global "profile" by default.

However I'm not sure how it works for invites.

This completely reworks MSC3189.

Signed-off-by: Robin Townsend <[email protected]>
@robintown robintown changed the title MSC3189: Per-room/per-space profile data MSC3189: Per-room/per-space profiles May 23, 2021
@robintown
Copy link
Member Author

robintown commented May 24, 2021

I just pushed a reworked version which hopefully resolves most concerns about room/space profiles not being first-class, so I'd be very grateful to get feedback on this again.

The new solution still scales O(n) with the number of rooms a profile change affects, though it is laid out in a way that should hopefully allow "upgrading" to O(1) updates with persona rooms in the future if desired, thereby separating concerns.

Copy link
Contributor

@kevincox kevincox left a comment

Choose a reason for hiding this comment

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

I think this is on the right track.

GET /_matrix/client/r0/profile/@me:example.org?scope=!space1:example.org

{
"inherits_from": "global",
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of a magic string "global" should we just use null to specify the global profile? This also has nice symmetry with omitting the scope URL param.

Copy link
Member Author

@robintown robintown Jun 21, 2021

Choose a reason for hiding this comment

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

Possibly, though I personally like having a self-explanatory keyword, and would like to avoid situations where undefined and null have not-so-subtle differences in meaning.

|to `inherits_from` C|All children of A that inherited from B recursively changed to inherit from C|All children of A that inherited from A recursively changed to inherit from C|
|to profile root|All children of A that inherited from B recursively changed to inherit from A|No change in inheritance|

### <a id="inheritance-restrictions"/>Inheritance restrictions
Copy link
Contributor

Choose a reason for hiding this comment

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

This is unfortunate. Why can't I create any random room to be my "alter ego" and configure arbitrary rooms to inherit from it?

Copy link
Member Author

@robintown robintown Jun 21, 2021

Choose a reason for hiding this comment

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

The intent of tying profile data to the space hierarchy is to avoid confusing users by introducing yet another perspective to organize one's rooms and spaces from. Also, I feel it would be weird to allow sibling rooms on otherwise equal footing to be able to inherit from each other - thus I believe that putting profile data in spaces is the most practical and user friendly option, given that users will generally already have rooms grouped together in a space when they want them to share profiles.

Copy link
Contributor

Choose a reason for hiding this comment

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

But I feel that this is leaving common use cases on the table. What if I have two profiles I use often and just want to pick one for each room? It seems weird that I can't do this if they don't align with spaces I am in.

I feel it would be weird to allow sibling rooms on otherwise equal footing to be able to inherit from each other

I don't think there is anything preventing cycles in spaces so while it may be "weird" it isn't actually avoiding any complexity in the algorithm. I don't think this is enough of a reason to deny it.

above inheritance conditions are upheld. For these reasons, servers implementing
per-room/per-space profiles must apply the following rules:

- When the user joins a space/room, perform a breadth-first search for an ancestor space that is a profile root, by following `m.space.child` links backwards through spaces the user has joined. Break any ties by selecting the first in a lexicographic ordering of room IDs, and then set the joined space/room to inherit from this profile root, or the global profile if none is found. This profile data should go immediately into the initial `join` state, rather than being updated after the join is complete.
Copy link
Contributor

Choose a reason for hiding this comment

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

If I understand the algorithm correctly it may prefer my global profile over a custom profile because the parent that was lexicographically first didn't have a custom profile set. Does it make more sense to say "The custom profile of the nearest parent with a custom profile (ties broken lexicographically). Falling back to the global profile if no parent has a custom profile set). I don't think that "nearest parent" is a perfect choice but it seems reasonable. The important part is that I think we should always prefer a custom profile in any parent over the global.

Furthermore while the auto-guess algorithm makes sense to have it should really be user choice. For example Element has a screen with some room info and a "Join" button. It would make sense to also have an indicator of what profile will be used and the option to select a different one. However one complication is that the client may not know which spaces a room is in until they join.

Copy link
Member Author

@robintown robintown Jun 21, 2021

Choose a reason for hiding this comment

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

Does it make more sense to say "The custom profile of the nearest parent with a custom profile (ties broken lexicographically).

Yes, this is essentially what I mean. The global profile is intended to be a last resort, only chosen if the server has traversed the room's entire ancestor graph and found no profile roots. (might see if I can reword this better)

Furthermore while the auto-guess algorithm makes sense to have it should really be user choice.

I also think it would be great UX-wise to allow clients to choose profiles before joining. Clients should have enough information to be able to tell which spaces a room could inherit from just by looking at the m.space.child links they can already see before joining (which for the purposes of this MSC, are what matter, not m.space.parent links). Augmenting the join APIs to support this is something that could easily be done in a follow-up MSC, so I'm going to call this out of scope for now.

per-room/per-space profiles must apply the following rules:

- When the user joins a space/room, perform a breadth-first search for an ancestor space that is a profile root, by following `m.space.child` links backwards through spaces the user has joined. Break any ties by selecting the first in a lexicographic ordering of room IDs, and then set the joined space/room to inherit from this profile root, or the global profile if none is found. This profile data should go immediately into the initial `join` state, rather than being updated after the join is complete.
- When the user leaves a space A, if A was a profile root, reset every space/room that inherited from A to inherit from `global`. Otherwise if A inherited from a space B, reset every child of A that inherited from B to inherit from `global`.
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems surprising! Maybe the custom profile should be "popped out" to its own room and references redirected.

Furthermore this makes me wonder if a space is the right place to store a profile. What if I want to use the same custom profile in two spaces? Maybe I work for CompanyA and we partner with CompanyB so I want to use my same "Work" custom profile for both.

I see the need to link the profiles to spaces, it is the more reasonable way to pick a default profile for new rooms, but I wonder if the profile data should actually live elsewhere. For example my profile is a room and it references which spaces it applies to. When changing that profile I change children of the referenced spaces. Then leaving a space can be updating the referenced spaces to remove the space itself and add the children (if desired).

Copy link
Member Author

@robintown robintown Jun 21, 2021

Choose a reason for hiding this comment

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

Furthermore this makes me wonder if a space is the right place to store a profile. What if I want to use the same custom profile in two spaces? Maybe I work for CompanyA and we partner with CompanyB so I want to use my same "Work" custom profile for both.

My assumption is that if a user wants to use the same profile across multiple spaces, they can either create a parent space ("Work") encompassing them both, or just maintain the two profiles independently. Neither option seems especially burdensome, especially in the case of two spaces.

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe the custom profile should be "popped out" to its own room and references redirected.

Hm, I'd feel weird about having a behavior where leaving a room causes you to spontaneously join a new one.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, I'd feel weird about having a behavior where leaving a room causes you to spontaneously join a new one.

I don't see why this is weird. I had a profile before. Now I still have a profile. Note that if as mentioned above these rooms aren't necessarily spaces they don't need to show up in my space list so it isn't weird to a user.

- When the user joins a space/room, perform a breadth-first search for an ancestor space that is a profile root, by following `m.space.child` links backwards through spaces the user has joined. Break any ties by selecting the first in a lexicographic ordering of room IDs, and then set the joined space/room to inherit from this profile root, or the global profile if none is found. This profile data should go immediately into the initial `join` state, rather than being updated after the join is complete.
- When the user leaves a space A, if A was a profile root, reset every space/room that inherited from A to inherit from `global`. Otherwise if A inherited from a space B, reset every child of A that inherited from B to inherit from `global`.
- When the user adds a space/room A to a space B, if A inherited from `global` and B is a profile root, set A to inherit from B. Otherwise if A inherited from `global` and B inherits from a space C, set A to inherit from C.
- When a space/room A is removed from a space B (whether by the user or someone else), if A inherited from a space C, check whether A is still allowed to inherit from C by [the above rules](#inheritance-restrictions). If this is no longer the case, then reset A to inherit from `global`.
Copy link
Contributor

Choose a reason for hiding this comment

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

This feels wrong to me. I would expect that the profile is sticky. I like the idea of using space membership to easily apply it to existing and future rooms but it seems that one applied it should stay until I explicitly change it.

Comment on lines +137 to +142
An alternative would be to store all per-room/per-space profile data in a single
global [extensible profile](https://github.com/matrix-org/matrix-doc/pull/1769),
essentially keeping a public mapping of room IDs → profile data. However, this
alternative would 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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe both of these could work together. I think that using spaces to apply profiles makes a lot of sense, and I think that this MSC is in the right direction, just a couple of minor tweaks. However I also think that storing the profiles in their own room makes a lot of sense, this allows sharing a profile between spaces and makes the behaviour simpler if you leave or are kicked from a space. So maybe they could work together to make an awesome solution.

  1. Use this algorithm for easy application of a profile to existing rooms and for picking a default profile for new rooms.
  2. Update 1769 to use separate rooms per-profile.
  3. Update this to reference the individual profile rooms.


## Security considerations

None that I am aware of.
Copy link
Contributor

Choose a reason for hiding this comment

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

  • User may think that setting a profile for a space hides their global profile from people who only know them from that space.
  • DMs "started from a space" will use the global profile.

Neither of these are new, but I think worth noting as they are unsolved and relevant.

@Mikaela
Copy link
Contributor

Mikaela commented Aug 25, 2021

I would like to ask what is the current status of per-space profiles, and have a couple of scenarios where this would be really helpful.

  • In Finnish speaking rooms I am often seeing critique towards people who have English pronouns in their display names (which to be honest would belong to bio metadata or similar that doesn't currently exist) and thus it would be nice to just be able to have a different display name in Matrix Suomi space.
  • Matrix is widely used for different communities. I would like to
    • be known as my real name, Mikaela Aminda Suomalainen in professional context, such as politics and some open source software projects
    • be known as Mikaela Aminda in less formal contexts
    • be known as Aminda (that I currently have as a global name) in Esperanto spaces and gaming specific spaces as that seems to be often my gaming tag.
    • additionally if I spoke Russian or similar language that doesn't use latin alphabet, I would like to e.g. cyrillize my name instead of using latin letters there.

Edited a bit as I legally took Aminda as my second first name in December 2021

@robintown
Copy link
Member Author

robintown commented Aug 25, 2021

Hi @Mikaela, those are all use cases that the general idea of per-room and per-space profiles was meant to solve, however this MSC is unfortunately blocked behind other spec work at the moment. After some discussions in the Matrix Spec room, I came to the conclusion that it would be best after all to wait until extensible profiles and sync v3 become more concrete, so that this MSC can take advantage of them. For that reason, I think I'll actually mark this MSC as a draft for the moment, because while the profile semantics proposed here will largely remain the same, the internals of this MSC will probably look quite different by the time those proposals become a reality. Sorry for the wait!

@20kdc
Copy link

20kdc commented Jan 8, 2022

I have a question, sorry if this is weird/addressed somehow:
What's the main advantage of this over documenting the existing client-event-managed system?
My assumption right now is it expands the work required on the part of homeservers, which will probably lead to more breakage, and since the existing system already uses extendable attributes, it's presumably ready for whatever clients put into profiles.

@tmonz
Copy link

tmonz commented Mar 11, 2022

that would be great. just had a couple of issues/delays, because some entities know me in one space under another nick, and then blocked my dm because they didn't know me by my 'standard' account name.

@Mikaela
Copy link
Contributor

Mikaela commented Mar 23, 2022

Has there been any development here or in the blockers, or is the best bet with pronouns #3755 ?

@robintown
Copy link
Member Author

This is unfortunately still blocked behind extensible profiles, peeking over federation, and changes to sync, so I don't think this is going to develop anytime soon.

@Gnuxie
Copy link
Contributor

Gnuxie commented Sep 8, 2022

Would it be possible to avoid the blockers on federation peeking/sync by instead proposing a change to the /profile api to optionally accept a room (in both client server and server server spec) (and return the profile). It is cheeky and means that you have to trust the profile owner's server to present the right state, but you will do that anyway as it is their profile. There is at least one downside in that this will become painful if portable accounts become a thing and federation peaking does become part of the spec, but that might be acceptable.

@eversteele
Copy link

Hi, looks like a year later since the last update on this... how is this coming along? Has it been folded into another MSC, perhaps? Thanks, in advance!

@tmonz
Copy link

tmonz commented Apr 15, 2023

One related question: within a space I've set up, I can change the nick in each room, but at the space-level I seem to keep the nick-name I had when setting up the space. How do I change that? Seems related to the MSC here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client-server Client-Server API kind:feature MSC for not-core and not-maintenance stuff needs-implementation This MSC does not have a qualifying implementation for the SCT to review. The MSC cannot enter FCP. proposal A matrix spec change proposal
Projects
None yet
Development

Successfully merging this pull request may close these issues.

API for changing displayname in a specific room (SPEC-230)