Skip to content

MSC4429: Profile Updates for Legacy Sync - #4429

Open
anoadragon453 wants to merge 7 commits into
mainfrom
anoa/legacy_sync_profile_updates
Open

anoadragon453 wants to merge 7 commits into
mainfrom
anoa/legacy_sync_profile_updates

Conversation

@anoadragon453

@anoadragon453 anoadragon453 commented Mar 5, 2026 •

Copy link
Copy Markdown
Member

Rendered

Conflict of Interest declaration: I am employed by Element. This MSC was written as part of my work on the Element Backend Team to land a "user status" feature (based on top of custom profiles, see MSC4426) for a customer.

Implementations:

  • Element Web (merged)
  • Synapse (include_profile_updates_in_sync option)

@anoadragon453 anoadragon453 changed the title MSCXXXX: Profile Updates for Legacy Sync MSC4429: Profile Updates for Legacy Sync Mar 5, 2026
@anoadragon453
anoadragon453 force-pushed the anoa/legacy_sync_profile_updates branch from 82ac3a7 to 3b205f6 Compare March 5, 2026 18:53
@anoadragon453
anoadragon453 marked this pull request as ready for review March 5, 2026 18:54
@anoadragon453 anoadragon453 added proposal A matrix spec change proposal. Process state. A-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. labels Mar 5, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Implementation requirements:

  • Client (using)
  • Server (sending)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comment thread proposals/4429-legacy-sync-profile-updates.md Outdated
spantaleev pushed a commit to spantaleev/matrix-docker-ansible-deploy that referenced this pull request May 29, 2026
Introduces the `matrix_synapse_experimental_features_msc4429_enabled`
variable (disabled by default), allowing Synapse to notify clients
using the legacy /sync endpoint of profile changes for other users.

See <matrix-org/matrix-spec-proposals#4429>

Signed-off-by: Norman Ziegner <n.ziegner@hzdr.de>
Comment thread proposals/4429-legacy-sync-profile-updates.md Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a new MSC proposing how to deliver custom profile field updates over legacy /sync (similar in spirit to MSC4262 for Sliding Sync), including opt-in filtering and guidance for initial population/lazy-loading.

Changes:

  • Proposes a new top-level users object in /sync carrying per-user profile_updates.
  • Introduces a new filter field (profile_fields.ids) to opt-in to specific profile field IDs.
  • Documents expected server/client behavior (initial sync behavior, lazy-loading interactions, implementation notes).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread proposals/4429-legacy-sync-profile-updates.md Outdated
Comment thread proposals/4429-legacy-sync-profile-updates.md
Comment thread proposals/4429-legacy-sync-profile-updates.md
Comment thread proposals/4429-legacy-sync-profile-updates.md
Comment thread proposals/4429-legacy-sync-profile-updates.md Outdated
Comment thread proposals/4429-legacy-sync-profile-updates.md
Comment on lines +71 to +73
Each entry in `profile_updates` represents a profile field and any changes to
its value. If its value is `null`, the field is treated as having been removed
from the user's profile (rather than literally being set to `null`).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@zecakeh pointed out on the equivalent Sliding Sync MSC that:

From the definition of PUT /profile/{userId}/{keyName}:

Servers MAY reject null values. Servers that accept null values SHOULD store them rather than treating null as a deletion request. Clients that want to delete a field, including its key and value, SHOULD use the DELETE endpoint instead.

So actually null is a valid value, and we can't use it as a signal to clients that a field has been deleted, unfortunately.

Instead, we'll probably want a removed_profile_fields array or similar:

{
  "users": {
    "@user:example.org": {
      "profile_updates": {
          "m.status": {
	        "text": "Swimming in the Great Lakes!",
	        "emoji": "🏊️"
	      },
      },
      "removed_profile_fields": ["m.other_field"]
    }
  }
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This sounds like it is re-treading conversations in #3391

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This sounds like it is re-treading conversations in #3391

I'm not sure, in this case there's already an established mechanism to delete a profile field (DELETE), that was about whether to add one.

What about either defining undefined as a deletion or else just changing it in this MSC so null is not an acceptable value? Having to add a separate field to track removed fields seems just unnecessary.

anoadragon453 and others added 2 commits July 2, 2026 11:21
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
{
"users": {
"@user:example.org": {
"profile_updates": {

@reivilibre reivilibre Jul 17, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It would be very beneficial if the server could flag the difference between a profile delta and a complete profile to replace whatever the client has in cache.

Without that distinction, the server is limited in how much clean-up it can do; it has to (at the very least) remember the whole set of 'removed profile fields' for all of time.

Why? Because we never know what state the client is in. If we want to clean up our deltas server-side, we will have to, at some point, tell clients 'you are too old to get just the delta, here's the whole profile instead'.
Since we can only currently speak in deltas, then sending a 'whole profile' involves tracking all field names that we might have sent to a client in the past.

Maybe the keys we reserve are something like:

  • profile: (fields)
  • profile_reset: true (marks this as a 'whole profile', not a delta. Should this be automatically implied for an initial sync?)
  • profile_remove: list of removed field names

You could, I suppose, make profile_remove: true the 'whole profile' marker. I don't know if that kind of double-duty is a good or bad thing. profile_reset and profile_remove do in fact clash so this 'naturally' resolves that clash by making it impossible

The field name `users` fits in alongside `rooms`. Using a nested
`profile_updates` dictionary allows extending the dictionary beneath each user
ID in the future if desired.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It is what it is, but profile_updates feels like a chunky key when it could have been called profile. It will be repeated a lot of times in the response body potentially.


### Clients

Clients are recommended to enable lazy-loading in their `/sync` requests to

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should include_redundant_members semantics apply also to profiles?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could you clarify what you mean here? Profile fields are sent when the profile updates, not with every event like member events?

@reivilibre reivilibre Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As far as I understand it, include_redundant_members means that when using lazy-loading sync, you get sent those user's membership events every time they appear in the timeline, not just the first time they appear in the timeline.
This is useful for clients that don't maintain a persistent cache of users, so they can always display the relevant information in-context without having to make CS API requests first.

The question is whether this exact same system should apply also to the profiles sent down when lazy-loading. You could imagine the same argument applying: clients might want to show the 'away' state next to the user's name and may not maintain a cache. The client might have been 're-launched' since witnessing the first timeline event from a given user, which is how they would have received their profile fields.

Comment on lines +77 to +78
of a given user; such as when a user no longer shares any room with another
user. For example:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I am partly wondering if this is actually necessary; would clients know to delete their profile cache because they already saw the user leave on their own?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Clients will not always know if they no longer share a room with a user, for instance if the room was lazy-loaded.

reivilibre added a commit to element-hq/synapse that referenced this pull request Aug 6, 2026
Implements support for [MSC4429: Profile Updates for Legacy
Sync](matrix-org/matrix-spec-proposals#4429).

Paired with matrix-org/complement#849 and
https://github.com/matrix-org/sytest/tree/anoa/msc4429

Tracking issue for removing unstable identifiers in Synapse:
#19891
Further improvements tracked in:
#19981

---------

Co-authored-by: Half-Shot <will@half-shot.uk>
Co-authored-by: Jason Robinson <jasonr@element.io>
Co-authored-by: Olivier 'reivilibre' <oliverw@element.io>
FrenchGithubUser pushed a commit to famedly/synapse that referenced this pull request Aug 13, 2026
Implements support for [MSC4429: Profile Updates for Legacy
Sync](matrix-org/matrix-spec-proposals#4429).

Paired with matrix-org/complement#849 and
https://github.com/matrix-org/sytest/tree/anoa/msc4429

Tracking issue for removing unstable identifiers in Synapse:
element-hq/synapse#19891
Further improvements tracked in:
element-hq/synapse#19981

---------

Co-authored-by: Half-Shot <will@half-shot.uk>
Co-authored-by: Jason Robinson <jasonr@element.io>
Co-authored-by: Olivier 'reivilibre' <oliverw@element.io>

None identified.

## Alternatives

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

More general: Why not put them into the member events like already done with displayname and avatar? Now we have two places to gather profile information.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Profile fields are global and not based on per-room member events so I don't believe tacking them onto existing member events would make sense.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

But who decides what is global? Shouldn't we consider to remove these information from the member event to have one single source of truth?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Putting them in per-room member events means we have the same ambiguity problems about what is intended to be global and what is intended to be per-room (e.g. per-room nicknames), so you don't know what you can and can't put in the user directory (for example) or use as a response to /profile.
The current profiles system (which is global) makes a lot of sense for this reason.

At the same time, removing the displayname and avatar from member events means that if the user's homeserver goes down, a new user joining the room won't know their name (or avatar). So there is some value in having these baked into the room state, even though the state bloat is undesirable.
(Either way, they can't be removed from room state yet for backwards compatibility reasons)

jaywink added a commit to element-hq/synapse that referenced this pull request Aug 18, 2026
Due to MSC4429 being in flux and clients having implemented field removals early, the previously merged implementation pushed field removals by setting the value to `null`. The MSC will require delivering the fields in a separate `removed_profile_fields` key as `None` is a valid field value, see matrix-org/matrix-spec-proposals#4429 (comment)

Thus, as we moved storing field deletes to `ProfileUpdateAction.DELETE`, we need to adjust the legacy sync to properly identify deletes. At the same time have implemented the new `removed_profile_fields` key, so we have it ready for simplifying the legacy sync response in the future.
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this pull request Sep 5, 2026
Tested on NetBSD 10 amd64 with 2026Q2 environment.

# Synapse 1.160.0 (2026-09-02)

## Features

- Add experimental support for [MSC4502](matrix-org/matrix-spec-proposals#4502): Targeted and unrestricted room member queries. ([\#19974](element-hq/synapse#19974))
- Add optional support for [MSC4262: Profile Updates for Sliding Sync](matrix-org/matrix-spec-proposals#4262).
  Currently defaults to disabled, and is limited to local users only for the sync results. ([\#20003](element-hq/synapse#20003))
- Allow specifying multiple `action_name` and `status` query parameters when listing scheduled tasks via the admin API. ([\#20067](element-hq/synapse#20067))

# Synapse 1.159.0 (2026-08-18)

## Features

- Add optional support for [MSC4429: Profile Updates for Legacy Sync](matrix-org/matrix-spec-proposals#4429).
  Currently defaults to not enabled, and is limited to local users only for the sync results. ([\#19556](element-hq/synapse#19556))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-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. Process state.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants