element-hq / element-hq/synapse

Simplified Sliding Sync drops a departed user's own membership from `required_state` after a history purge

Open
#19,884 0 comments 2 reactions 0 assignees View on GitHub
A-Leave A-Message-Retention-Policies A-Retention A-Sync T-Defect
Dominant language
Python
Stars
4.6k
Forks
600
Avg merge
5d 22h
Merged PRs (30d)
51

Description

*As discussed in [`#synapse-dev:matrix.org`](https://matrix.to/#/!i5D5LLct_DYG-4hQprLzrxdbZ580U9UB6AEgFnk6rZQ/$SWE8eJCT9uEvSmAzURDMuwWCEWwpHARu7ZJmSWkCM5w?via=element.io&via=matrix.org&via=beeper.com),*

### Description

After `purge_history` runs over the point in a room's timeline where a user left / was kicked / was banned, Simplified Sliding Sync (MSC4186 / `org.matrix.simplified_msc3575`) stops returning that user's own `m.room.member` event in the room's `required_state` even though the membership is still durably recorded in current-state tables. The room is still returned in the response, just with no `$ME` membership event.

### Steps to reproduce

I created a Complement test which reproduces the problem: https://github.com/codyfinn/synapse/blob/c117852b8aa27ec01996c8cc28690cc268a22203/complement/tests/sliding_sync_purged_membership_regression_test.go

Manual steps:

- Register `@alice` (room admin) and `@bob`.
- As `@alice`, create a public room: `POST /_matrix/client/v3/createRoom {"preset": "public_chat"}` → `!room:hs1`.
- As `@bob`, join: `POST /_matrix/client/v3/join/!room:hs1`. Wait for the join to sync.
- As `@alice`, send a few `m.room.message` events so there is joined-era history before the departure (gives the purge something to outlier behind).
- Depart `@bob` — any of the three reproduces:
- kick: `POST /_matrix/client/v3/rooms/!room:hs1/kick {"user_id": "@bob:hs1"}`
- ban: `POST /_matrix/client/v3/rooms/!room:hs1/ban {"user_id": "@bob:hs1"}`
- leave: `POST /_matrix/client/v3/rooms/!room:hs1/leave` (as `@bob`)
- As `@alice`, send one more `m.room.message` *after* the departure and record its `event_id` (`$boundary`). This is the purge boundary — everything topologically before it (including bob's `m.room.member` event) will be purged.
- As admin, purge history up to the boundary:
- `POST /_synapse/admin/v1/purge_history/!room:hs1` with `{"purge_up_to_event_id": "$boundary", "delete_local_events": true}`
- poll `GET /_synapse/admin/v1/purge_history_status/{purge_id}` until `"status": "complete"`
- As `@bob`, issue a Simplified Sliding Sync request asking for his own membership:
```jsonc
POST /_matrix/client/unstable/org.matrix.simplified_msc3575/sync
{
"lists": { "all": { "ranges": [[0, 99]],
"required_state": [["m.room.member", "@bob:hs1"]], "timeline_limit": 1 } },
"room_subscriptions": { "!room:hs1": {
"required_state": [["m.room.member", "@bob:hs1"]], "timeline_limit": 1 } }
}
```

### Homeserver

A personal homeserver

### Synapse Version

1.155.0

### Installation Method

Docker (matrixdotorg/synapse)

### Database

PostgreSQL single server. Never ran SQLite. Never restored from a backup

### Workers

Multiple workers

### Platform

Reproduced on Complement Synapse image — Debian trixie, Python 3.13, running under Docker on macOS

Running in a Kubernetes deployment

### Configuration

_No response_

### Relevant log output

I can't share server logs, but it is reproducible with a Complement test found here: https://github.com/codyfinn/synapse/blob/c117852b8aa27ec01996c8cc28690cc268a22203/complement/tests/sliding_sync_purged_membership_regression_test.go

### Anything else that would be useful to know?

_No response_

Contributor guide

Open the contributing guide

Research direction

Start with complement/tests/sliding_sync_purged_membership_regression_test.go and run the reproduction against Synapse. Trace the Simplified Sliding Sync handling for the requested m.room.member state after the purge scenario. Done means the room response includes the departed user's own membership event in required_state for kick, ban, and leave cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.