From 1639c3028144d7b382ea299705e1f5f9f067ac14 Mon Sep 17 00:00:00 2001 From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Date: Tue, 30 Jun 2026 12:46:47 +0100 Subject: [PATCH 1/5] Fix the `cargo-test` and `cargo-bench` CI jobs not running (#19883) --- .github/workflows/fix_lint.yaml | 4 ++- .github/workflows/tests.yml | 26 ++++++++++++--- changelog.d/19883.misc | 1 + rust/src/events/formats/v4.rs | 4 ++- rust/src/events/formats/vmsc4242.rs | 4 ++- rust/src/events/utils.rs | 21 ++++++------ rust/src/json.rs | 12 +++---- tests/events/test_validator.py | 50 ++++++++++++++++++++++++++++- 8 files changed, 97 insertions(+), 25 deletions(-) create mode 100644 changelog.d/19883.misc diff --git a/.github/workflows/fix_lint.yaml b/.github/workflows/fix_lint.yaml index 3e518c6267..e0817698f4 100644 --- a/.github/workflows/fix_lint.yaml +++ b/.github/workflows/fix_lint.yaml @@ -9,7 +9,9 @@ on: env: # We use nightly so that `fmt` correctly groups together imports, and # clippy correctly fixes up the benchmarks. - RUST_VERSION: nightly-2025-06-24 + # + # Note: This should match the nightly rust version in `tests.yml`. + RUST_VERSION: nightly-2025-03-27 jobs: fixup: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6ecdd88815..aa6250612b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,6 +13,16 @@ concurrency: env: RUST_VERSION: 1.87.0 + # This nightly is roughly the last to be branded 1.87.0. + # + # We know this, as 1.87.0 was branched from master on 2025-03-28. Shortly + # afterwards, nightlies were then branded as 1.88.0. See + # https://releases.rs/docs/1.87.0/ for where we get the dates. + # + # Technically the last 1.87.0 nightly was 2025-03-29, but that all depends on + # at which time of day they cut the release and when the nightly is built. + # It's safer for future releases to just do the day before. + RUST_NIGHTLY_VERSION: nightly-2025-03-27 # last nightly before 1.88.0 jobs: # Job to detect what has changed so we don't run e.g. Rust checks on PRs that @@ -240,7 +250,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master with: - toolchain: nightly-2026-02-01 + toolchain: ${{ env.RUST_NIGHTLY_VERSION }} components: clippy - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 @@ -296,7 +306,7 @@ jobs: with: # We use nightly so that we can use some unstable options that we use in # `.rustfmt.toml`. - toolchain: nightly-2025-04-23 + toolchain: ${{ env.RUST_NIGHTLY_VERSION }} components: rustfmt - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 @@ -677,7 +687,12 @@ jobs: - changes cargo-test: - if: ${{ needs.changes.outputs.rust == 'true' }} + # We need to explicitly specify `!cancelled() && !failure()` as otherwise + # GitHub will implicitly add `success()`, which will be `false` when any job + # in `needs`, or its dependents, is skipped. Most notably, jobs like + # `lint-readme`, a dependency of `linting-done`, are unlikely to run very + # often - and would result in this job being skipped. + if: ${{ !cancelled() && !failure() && needs.changes.outputs.rust == 'true' }} runs-on: ubuntu-latest needs: - linting-done @@ -697,7 +712,8 @@ jobs: # We want to ensure that the cargo benchmarks still compile, which requires a # nightly compiler. cargo-bench: - if: ${{ needs.changes.outputs.rust == 'true' }} + # See `cargo-test` above for why we need to specify `!cancelled() && !failure()`. + if: ${{ !cancelled() && !failure() && needs.changes.outputs.rust == 'true' }} runs-on: ubuntu-latest needs: - linting-done @@ -709,7 +725,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master with: - toolchain: nightly-2022-12-01 + toolchain: ${{ env.RUST_NIGHTLY_VERSION }} - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - run: cargo bench --no-run diff --git a/changelog.d/19883.misc b/changelog.d/19883.misc new file mode 100644 index 0000000000..673e8cea1a --- /dev/null +++ b/changelog.d/19883.misc @@ -0,0 +1 @@ +Prevent the `cargo-deny` and `cargo-bench` CI jobs from being skipped, even on PRs that have Rust changes. \ No newline at end of file diff --git a/rust/src/events/formats/v4.rs b/rust/src/events/formats/v4.rs index 22a1a677f2..08e5189275 100644 --- a/rust/src/events/formats/v4.rs +++ b/rust/src/events/formats/v4.rs @@ -37,7 +37,7 @@ use crate::{ json::AllowMissing, }; -/// Version-specific fields for room version 11. +/// Version-specific fields for room version 12+. #[derive(Serialize, Deserialize)] pub struct EventFormatV4 { #[serde( @@ -57,6 +57,8 @@ impl EventFormatV4 { bail!("v4 events must not have an explicit event_id"); } + validate_optional_room_id(self.room_id.as_ref_opt(), common_fields)?; + Ok(()) } diff --git a/rust/src/events/formats/vmsc4242.rs b/rust/src/events/formats/vmsc4242.rs index 45d9a25068..4e13e6f36b 100644 --- a/rust/src/events/formats/vmsc4242.rs +++ b/rust/src/events/formats/vmsc4242.rs @@ -33,7 +33,7 @@ use pyo3::PyResult; use serde::{Deserialize, Serialize}; use crate::events::constants::event_type::M_ROOM_CREATE; -use crate::events::formats::v4::get_room_id_for_optional_room_id; +use crate::events::formats::v4::{get_room_id_for_optional_room_id, validate_optional_room_id}; use crate::events::formats::EventCommonFields; use crate::events::Event; use crate::json::AllowMissing; @@ -62,6 +62,8 @@ impl EventFormatVMSC4242 { bail!("MSC4242 events must not have an explicit event_id"); } + validate_optional_room_id(self.room_id.as_ref_opt(), common_fields)?; + Ok(()) } diff --git a/rust/src/events/utils.rs b/rust/src/events/utils.rs index b58edc9352..668e415d5b 100644 --- a/rust/src/events/utils.rs +++ b/rust/src/events/utils.rs @@ -367,9 +367,14 @@ mod tests { } #[test] - /// Tests to ensure events with overly large values for `depth` are handled appropriately. - /// This was added in room version 6 . - fn test_calculate_event_id_big_int_old_rooms() { + /// This is a bit funky, but we test that relaxed `depth` rules are in + /// place, even in room versions that enforce strict canonical JSON, as we + /// still need to load invalid events from the database even in newer room + /// versions. See https://github.com/element-hq/synapse/pull/19816. + /// + /// We still enforce canonicaljson when creating *new* events (see + /// `EventValidator` on the python side). + fn test_calculate_event_id_big_int_is_relaxed() { let original = json!( { "auth_events":[ @@ -407,12 +412,10 @@ mod tests { ); // These should succeed. - let _event_id = calculate_event_id(&original, &RoomVersion::V3).unwrap(); - let _event_id = calculate_event_id(&original, &RoomVersion::V4).unwrap(); - let _event_id = calculate_event_id(&original, &RoomVersion::V5).unwrap(); - - // These should not succeed. let versions = [ + RoomVersion::V3, + RoomVersion::V4, + RoomVersion::V5, RoomVersion::V6, RoomVersion::V7, RoomVersion::V8, @@ -422,7 +425,7 @@ mod tests { RoomVersion::V12, ]; for version in versions { - let _event_id = calculate_event_id(&original, &version).unwrap_err(); + let _event_id = calculate_event_id(&original, &version).unwrap(); } } diff --git a/rust/src/json.rs b/rust/src/json.rs index 3e833c6707..2a788e5e61 100644 --- a/rust/src/json.rs +++ b/rust/src/json.rs @@ -105,8 +105,6 @@ pub mod allow_missing { #[cfg(test)] mod tests { - use std::assert_matches; - use serde::{Deserialize, Serialize}; use super::*; @@ -126,12 +124,12 @@ mod tests { let json = r#"{"value":42}"#; let deserialized: TestStruct = serde_json::from_str(json).unwrap(); assert!(deserialized.value.is_some()); - assert_matches!(deserialized.value, AllowMissing::Some(42)); + assert!(matches!(deserialized.value, AllowMissing::Some(42))); let json = r#"{}"#; let deserialized: TestStruct = serde_json::from_str(json).unwrap(); assert!(deserialized.value.is_absent()); - assert_matches!(deserialized.value, AllowMissing::Absent); + assert!(matches!(deserialized.value, AllowMissing::Absent)); } #[test] @@ -203,16 +201,16 @@ mod tests { let json = r#"{"value":42}"#; let deserialized: TestStructOption = serde_json::from_str(json).unwrap(); assert!(deserialized.value.is_some()); - assert_matches!(deserialized.value, AllowMissing::Some(Some(42))); + assert!(matches!(deserialized.value, AllowMissing::Some(Some(42)))); let json = r#"{"value":null}"#; let deserialized: TestStructOption = serde_json::from_str(json).unwrap(); assert!(deserialized.value.is_some()); - assert_matches!(deserialized.value, AllowMissing::Some(None)); + assert!(matches!(deserialized.value, AllowMissing::Some(None))); let json = r#"{}"#; let deserialized: TestStructOption = serde_json::from_str(json).unwrap(); assert!(deserialized.value.is_absent()); - assert_matches!(deserialized.value, AllowMissing::Absent); + assert!(matches!(deserialized.value, AllowMissing::Absent)); } } diff --git a/tests/events/test_validator.py b/tests/events/test_validator.py index 082ae04a4c..2b1ee931ba 100644 --- a/tests/events/test_validator.py +++ b/tests/events/test_validator.py @@ -11,7 +11,12 @@ # See the GNU Affero General Public License for more details: # . # -from synapse.api.room_versions import RoomVersions +from synapse.api.errors import Codes, SynapseError +from synapse.api.room_versions import ( + KNOWN_ROOM_VERSIONS, + EventFormatVersions, + RoomVersions, +) from synapse.events import make_event_from_dict from synapse.events.validator import EventValidator @@ -45,3 +50,46 @@ class EventValidatorTestCase(HomeserverTestCase): ) EventValidator().validate_new(event, self.hs.config) + + def test_validate_new_rejects_big_depth_for_strict_canonicaljson_rooms( + self, + ) -> None: + """ + Test that `EventValidator.validate_new` rejects events with integers outside the + canonical JSON range, in room versions which enforce it (v6+). + """ + for room_version in KNOWN_ROOM_VERSIONS.values(): + with self.subTest(room_version=room_version.identifier): + event_dict = { + "room_id": "!room:test", + "type": "m.room.message", + "sender": "@alice:example.com", + "content": { + "msgtype": "m.text", + "body": "hello", + }, + "auth_events": [], + "prev_events": [], + "hashes": {"sha256": "aGVsbG8="}, + "signatures": {}, + "depth": 2**53, + "origin_server_ts": 1000, + } + + if room_version.event_format == EventFormatVersions.ROOM_V1_V2: + event_dict["event_id"] = "$event:test" + + event = make_event_from_dict( + event_dict, + room_version=room_version, + ) + + # Check if this room version enforces strict canonical json. + if room_version.strict_canonicaljson: + with self.assertRaises(SynapseError) as cm: + EventValidator().validate_new(event, self.hs.config) + + self.assertEqual(cm.exception.errcode, Codes.BAD_JSON) + self.assertEqual(cm.exception.msg, "JSON integer out of range") + else: + EventValidator().validate_new(event, self.hs.config) From aa6e6164706ea562d5180d8b5ea475658ac63675 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Tue, 30 Jun 2026 14:24:42 +0100 Subject: [PATCH 2/5] Don't delete the dehydrated device when MAS syncs the device list (#19892) `_synapse/mas/sync_devices` checks the device list against the set of devices MAS knows about, but the dehydrated device (MSC3814) is invisible to MAS, so it gets automatically deleted on each sync, which prevents dehydrated devices from working. This change excludes the dehydrated device from the check. There is similar special case code in the admin devices API (which gives it a special flag) and MAS's own legacy sync path (which filters it out). This code was initially written by @ara4n and Claude, but both he and I have read it and think it makes sense. I am far from a Synapse expert, so feel free to tell me it's all wrong and point me in the right direction. A system-level test for this bug is being written here: https://github.com/element-hq/element-web/issues/34034 but if you think we should have one somewhere else, please let me know. Closes https://github.com/element-hq/synapse/issues/19889 ### Pull Request Checklist * [x] Pull request is based on the develop branch * [x] Pull request includes a [changelog file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog). The entry should: * [x] [Code style](https://element-hq.github.io/synapse/latest/code_style.html) is correct (run the [linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters)) Co-authored-by: Matthew --- changelog.d/19892.bugfix | 1 + synapse/rest/synapse/mas/devices.py | 10 ++++++ tests/rest/synapse/mas/test_devices.py | 42 ++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 changelog.d/19892.bugfix diff --git a/changelog.d/19892.bugfix b/changelog.d/19892.bugfix new file mode 100644 index 0000000000..9439433274 --- /dev/null +++ b/changelog.d/19892.bugfix @@ -0,0 +1 @@ +Fix a bug where a user's dehydrated device (MSC3814) was deleted when their device list was synced from MAS (e.g. on logging out their last device), breaking offline key delivery. diff --git a/synapse/rest/synapse/mas/devices.py b/synapse/rest/synapse/mas/devices.py index 9d94a67675..b5d19d3cec 100644 --- a/synapse/rest/synapse/mas/devices.py +++ b/synapse/rest/synapse/mas/devices.py @@ -196,6 +196,16 @@ class MasSyncDevicesResource(MasBaseResource): current_devices_list = set(current_devices.keys()) target_device_list = set(body.devices) + # Exclude the dehydrated device (MSC3814): it has no MAS session, so MAS + # never lists it in the target set and the reconciliation below would + # otherwise treat it as extra and delete it. This mirrors the admin + # devices API and MAS's own legacy device-sync path, which both skip it. + dehydrated_device = await self.device_handler.get_dehydrated_device( + user_id=str(user_id) + ) + if dehydrated_device is not None: + current_devices_list.discard(dehydrated_device[0]) + to_add = target_device_list - current_devices_list to_delete = current_devices_list - target_device_list diff --git a/tests/rest/synapse/mas/test_devices.py b/tests/rest/synapse/mas/test_devices.py index 6b7596f1c6..e9035f4c0b 100644 --- a/tests/rest/synapse/mas/test_devices.py +++ b/tests/rest/synapse/mas/test_devices.py @@ -546,6 +546,48 @@ class MasSyncDevicesResource(BaseTestCase): devices = self.get_success(store.get_devices_by_user(str(self.alice_user_id))) self.assertEqual(set(devices.keys()), {"DEVICE1"}) + def test_sync_devices_preserves_dehydrated_device(self) -> None: + # Store a dehydrated device (MSC3814). It has no MAS session, so it is + # never part of the target device list, but it must survive a sync. + device_handler = self.hs.get_device_handler() + dehydrated_device_id = self.get_success( + device_handler.store_dehydrated_device( + user_id=str(self.alice_user_id), + device_id=None, + device_data={"device_data": {"foo": "bar"}}, + initial_device_display_name="dehydrated device", + keys_for_device={}, + ) + ) + + # Sync down to a single real device. The dehydrated device is not in the + # target list, but must not be deleted. + channel = self.make_request( + "POST", + "/_synapse/mas/sync_devices", + shorthand=False, + access_token=self.SHARED_SECRET, + content={ + "localpart": "alice", + "devices": ["DEVICE1"], + }, + ) + + self.assertEqual(channel.code, 200, channel.json_body) + self.assertEqual(channel.json_body, {}) + + # The real devices are reconciled, but the dehydrated device survives. + store = self.hs.get_datastores().main + devices = self.get_success(store.get_devices_by_user(str(self.alice_user_id))) + self.assertEqual(set(devices.keys()), {"DEVICE1", dehydrated_device_id}) + + # And it is still registered as the dehydrated device. + dehydrated = self.get_success( + device_handler.get_dehydrated_device(str(self.alice_user_id)) + ) + assert dehydrated is not None + self.assertEqual(dehydrated[0], dehydrated_device_id) + def test_sync_devices_add_and_delete(self) -> None: # Sync with a mix of additions and deletions channel = self.make_request( From a247001400cec3551d05281ce36d4427d6e39213 Mon Sep 17 00:00:00 2001 From: Olivier 'reivilibre Date: Tue, 30 Jun 2026 14:41:26 +0100 Subject: [PATCH 3/5] 1.156.0rc1 --- CHANGES.md | 47 +++++++++++++++++++++++++++++++ changelog.d/19390.bugfix | 1 - changelog.d/19591.feature | 1 - changelog.d/19646.bugfix | 1 - changelog.d/19660.doc | 1 - changelog.d/19732.misc | 1 - changelog.d/19758.feature | 1 - changelog.d/19762.feature | 2 -- changelog.d/19785.bugfix | 1 - changelog.d/19788.doc | 1 - changelog.d/19794.feature | 1 - changelog.d/19810.bugfix | 1 - changelog.d/19829.doc | 1 - changelog.d/19832.misc | 1 - changelog.d/19834.bugfix | 1 - changelog.d/19840.misc | 1 - changelog.d/19843.misc | 1 - changelog.d/19845.bugfix | 1 - changelog.d/19847.doc | 1 - changelog.d/19848.feature | 1 - changelog.d/19849.misc | 1 - changelog.d/19850.bugfix | 1 - changelog.d/19853.feature | 3 -- changelog.d/19854.misc | 1 - changelog.d/19855.bugfix | 1 - changelog.d/19856.bugfix | 1 - changelog.d/19866.misc | 1 - changelog.d/19868.misc | 1 - changelog.d/19869.bugfix | 1 - changelog.d/19874.feature | 1 - changelog.d/19877.misc | 1 - changelog.d/19883.misc | 1 - changelog.d/19892.bugfix | 1 - debian/changelog | 5 ++-- pyproject.toml | 2 +- schema/synapse-config.schema.yaml | 2 +- 36 files changed, 52 insertions(+), 39 deletions(-) delete mode 100644 changelog.d/19390.bugfix delete mode 100644 changelog.d/19591.feature delete mode 100644 changelog.d/19646.bugfix delete mode 100644 changelog.d/19660.doc delete mode 100644 changelog.d/19732.misc delete mode 100644 changelog.d/19758.feature delete mode 100644 changelog.d/19762.feature delete mode 100644 changelog.d/19785.bugfix delete mode 100644 changelog.d/19788.doc delete mode 100644 changelog.d/19794.feature delete mode 100644 changelog.d/19810.bugfix delete mode 100644 changelog.d/19829.doc delete mode 100644 changelog.d/19832.misc delete mode 100644 changelog.d/19834.bugfix delete mode 100644 changelog.d/19840.misc delete mode 100644 changelog.d/19843.misc delete mode 100644 changelog.d/19845.bugfix delete mode 100644 changelog.d/19847.doc delete mode 100644 changelog.d/19848.feature delete mode 100644 changelog.d/19849.misc delete mode 100644 changelog.d/19850.bugfix delete mode 100644 changelog.d/19853.feature delete mode 100644 changelog.d/19854.misc delete mode 100644 changelog.d/19855.bugfix delete mode 100644 changelog.d/19856.bugfix delete mode 100644 changelog.d/19866.misc delete mode 100644 changelog.d/19868.misc delete mode 100644 changelog.d/19869.bugfix delete mode 100644 changelog.d/19874.feature delete mode 100644 changelog.d/19877.misc delete mode 100644 changelog.d/19883.misc delete mode 100644 changelog.d/19892.bugfix diff --git a/CHANGES.md b/CHANGES.md index 70a9b60e4f..58a061bd43 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,50 @@ +# Synapse 1.156.0rc1 (2026-06-30) + +## Features + +- Expose [MSC4354 Sticky Events](https://github.com/matrix-org/matrix-spec-proposals/pull/4354) over [MSC4186 (Simplified) Sliding Sync](https://github.com/matrix-org/matrix-spec-proposals/pull/4186). ([\#19591](https://github.com/element-hq/synapse/issues/19591)) +- Stabilize support for sending ephemeral events to application services, as per [MSC2409](https://github.com/matrix-org/matrix-spec-proposals/pull/2409). Contributed by @jason-famedly @ Famedly. ([\#19758](https://github.com/element-hq/synapse/issues/19758)) +- Include `allowed_room_ids` in the `/summary` client-server API response for rooms with restricted join rules, as required by Matrix 1.15. + Contributed by @FrenchGithubUser @Famedly. ([\#19762](https://github.com/element-hq/synapse/issues/19762)) +- [MSC4140: Cancellable delayed events](https://github.com/matrix-org/matrix-spec-proposals/pull/4140): Allow authentication on delayed event management endpoints (such as `/restart`) to bypass ratelimits for unauthenticated requests based on the client IP address. ([\#19794](https://github.com/element-hq/synapse/issues/19794)) +- Add new metric `synapse_non_deactivated_user_count` which tracks the number of non-deactivated users in the database, split by `app_service`. ([\#19848](https://github.com/element-hq/synapse/issues/19848)) +- The `GET /_matrix/client/unstable/org.matrix.msc1763/retention/configuration` endpoint is now provided when retention + is enabled and `experimental_features.msc1763_enabled` is enabled, based on + [MSC1763](https://github.com/matrix-org/matrix-spec-proposals/pull/1763). ([\#19853](https://github.com/element-hq/synapse/issues/19853)) +- Add experimental support for [MSC4491: Invite reasons in room creation](https://github.com/matrix-org/matrix-spec-proposals/pull/4491). ([\#19874](https://github.com/element-hq/synapse/issues/19874)) + +## Bugfixes + +- Provide remote servers a way to find out about an event created during the remote join handshake. Contributed by @FrenchGithubUser and @jason-famedly @ Famedly. ([\#19390](https://github.com/element-hq/synapse/issues/19390), [\#19855](https://github.com/element-hq/synapse/issues/19855), [\#19856](https://github.com/element-hq/synapse/issues/19856)) +- Advertise `org.matrix.msc4143` in `unstable_features` when `msc4143_enabled` is set. ([\#19646](https://github.com/element-hq/synapse/issues/19646)) +- Fix a long-standing bug where the badge notification count for a room could become permanently inflated if a read receipt was sent before the room's notification counts were first summarised. ([\#19785](https://github.com/element-hq/synapse/issues/19785)) +- Fix startup listener logging to report the actual bound TCP port, so listeners configured with port `0` no longer log `Synapse now listening on TCP port 0`. ([\#19810](https://github.com/element-hq/synapse/issues/19810)) +- Fix notification counts being inflated after a `/purge_history` when notifications had already been rotated into the summary table. ([\#19834](https://github.com/element-hq/synapse/issues/19834)) +- Fix `/sync` caching transient errors for the `sync_response_cache_duration`. ([\#19845](https://github.com/element-hq/synapse/issues/19845)) +- Fix local events being deleted by the [Purge History admin API](https://element-hq.github.io/synapse/v1.155/admin_api/purge_history_api.html) despite `delete_local_events` being set to false, in room versions other than 1 and 2. ([\#19850](https://github.com/element-hq/synapse/issues/19850)) +- Fix a bug where a user's dehydrated device ([MSC3814](https://github.com/matrix-org/matrix-spec-proposals/pull/3814)) was deleted when their device list was synced from Matrix Authentication Service (e.g. upon logging out their last device), breaking offline key delivery. ([\#19892](https://github.com/element-hq/synapse/issues/19892)) + +## Improved Documentation + +- Update `auto_join_rooms` config documentation to cover requirements for auto-joining invite-only rooms. ([\#19660](https://github.com/element-hq/synapse/issues/19660)) +- Add stable endpoint for [MSC3266: Room summary API](https://github.com/matrix-org/matrix-spec-proposals/pull/3266) into worker docs. Contributed by @olmari. ([\#19788](https://github.com/element-hq/synapse/issues/19788)) +- Tweak wording of Rust crate dependency update policy. ([\#19829](https://github.com/element-hq/synapse/issues/19829)) +- Fixed the Admin API user endpoint documentation examples to use JSON booleans (true/false) instead of numeric (0/1) values. ([\#19847](https://github.com/element-hq/synapse/issues/19847)) + +## Internal Changes + +- Make `simple_select_one_onecol_txn()` more helpful by naming the table of the select - as all other query wrapper functions already did. ([\#19869](https://github.com/element-hq/synapse/issues/19869)) +- Refactor `get_user_which_could_invite` logic to reuse `get_users_which_can_issue_invite`. Contributed by Noah Markert. ([\#19732](https://github.com/element-hq/synapse/issues/19732)) +- Fix a flaky test (`twisted.protocols.amp.TooLong` error under `trial -jN`) caused by an oversized debug log line. ([\#19832](https://github.com/element-hq/synapse/issues/19832)) +- Upload Complement test logs as CI artifacts instead of printing the raw output to the build log. ([\#19840](https://github.com/element-hq/synapse/issues/19840)) +- Fix release script considering any workflow completion as successful. ([\#19843](https://github.com/element-hq/synapse/issues/19843)) +- Force keyword-args for clear `default_config(server_name="test")` usage in test utilities. ([\#19849](https://github.com/element-hq/synapse/issues/19849)) +- Add `.ruff_cache/` directory to `.gitignore`. ([\#19854](https://github.com/element-hq/synapse/issues/19854)) +- Bump `poetry` in CI from `2.2.1` to `2.4.1`. ([\#19866](https://github.com/element-hq/synapse/issues/19866), [\#19877](https://github.com/element-hq/synapse/issues/19877)) +- Split out `deferred` and `tokio_runtime` to their own Rust modules. ([\#19868](https://github.com/element-hq/synapse/issues/19868)) +- Prevent the `cargo-deny` and `cargo-bench` CI jobs from being skipped, even on PRs that have Rust changes. ([\#19883](https://github.com/element-hq/synapse/issues/19883)) + + # Synapse 1.155.0 (2026-06-16) ## End of Life of Debian 12 Bookworm diff --git a/changelog.d/19390.bugfix b/changelog.d/19390.bugfix deleted file mode 100644 index 706f779174..0000000000 --- a/changelog.d/19390.bugfix +++ /dev/null @@ -1 +0,0 @@ -Provide remote servers a way to find out about an event created during the remote join handshake. Contributed by @FrenchGithubUser and @jason-famedly @ Famedly. diff --git a/changelog.d/19591.feature b/changelog.d/19591.feature deleted file mode 100644 index f780030807..0000000000 --- a/changelog.d/19591.feature +++ /dev/null @@ -1 +0,0 @@ -Expose [MSC4354 Sticky Events](https://github.com/matrix-org/matrix-spec-proposals/pull/4354) over [MSC4186 (Simplified) Sliding Sync](https://github.com/matrix-org/matrix-spec-proposals/pull/4186). \ No newline at end of file diff --git a/changelog.d/19646.bugfix b/changelog.d/19646.bugfix deleted file mode 100644 index 5c8d505e87..0000000000 --- a/changelog.d/19646.bugfix +++ /dev/null @@ -1 +0,0 @@ -Advertise `org.matrix.msc4143` in `unstable_features` when `msc4143_enabled` is set. diff --git a/changelog.d/19660.doc b/changelog.d/19660.doc deleted file mode 100644 index f03bc6090c..0000000000 --- a/changelog.d/19660.doc +++ /dev/null @@ -1 +0,0 @@ -Update `auto_join_rooms` config documentation to cover requirements for auto-joining invite-only rooms. diff --git a/changelog.d/19732.misc b/changelog.d/19732.misc deleted file mode 100644 index 99fca0a018..0000000000 --- a/changelog.d/19732.misc +++ /dev/null @@ -1 +0,0 @@ -Refactor `get_user_which_could_invite` logic to reuse `get_users_which_can_issue_invite`. Contributed by Noah Markert. diff --git a/changelog.d/19758.feature b/changelog.d/19758.feature deleted file mode 100644 index 5a360c7695..0000000000 --- a/changelog.d/19758.feature +++ /dev/null @@ -1 +0,0 @@ -Stabilize support for sending ephemeral events to application services, as per [MSC2409](https://github.com/matrix-org/matrix-spec-proposals/pull/2409). Contributed by @jason-famedly @ Famedly. diff --git a/changelog.d/19762.feature b/changelog.d/19762.feature deleted file mode 100644 index 543db3184b..0000000000 --- a/changelog.d/19762.feature +++ /dev/null @@ -1,2 +0,0 @@ -Include `allowed_room_ids` in the `/summary` client-server API response for rooms with restricted join rules, as required by Matrix 1.15. -Contributed by @FrenchGithubUser @Famedly. diff --git a/changelog.d/19785.bugfix b/changelog.d/19785.bugfix deleted file mode 100644 index 643481ee27..0000000000 --- a/changelog.d/19785.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix a long-standing bug where the badge notification count for a room could become permanently inflated if a read receipt was sent before the room's notification counts were first summarised. diff --git a/changelog.d/19788.doc b/changelog.d/19788.doc deleted file mode 100644 index bfdd3a3d37..0000000000 --- a/changelog.d/19788.doc +++ /dev/null @@ -1 +0,0 @@ -Add stable endpoint for [MSC3266: Room summary API](https://github.com/matrix-org/matrix-spec-proposals/pull/3266) into worker docs. Contributed by @olmari. \ No newline at end of file diff --git a/changelog.d/19794.feature b/changelog.d/19794.feature deleted file mode 100644 index be373f9a77..0000000000 --- a/changelog.d/19794.feature +++ /dev/null @@ -1 +0,0 @@ -[MSC4140: Cancellable delayed events](https://github.com/matrix-org/matrix-spec-proposals/pull/4140): Allow authentication on delayed event management endpoints (such as `/restart`) to bypass ratelimits for unauthenticated requests based on the client IP address. diff --git a/changelog.d/19810.bugfix b/changelog.d/19810.bugfix deleted file mode 100644 index 72592532f8..0000000000 --- a/changelog.d/19810.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix startup listener logging to report the actual bound TCP port, so listeners configured with port `0` no longer log `Synapse now listening on TCP port 0`. diff --git a/changelog.d/19829.doc b/changelog.d/19829.doc deleted file mode 100644 index 44dbc0eff4..0000000000 --- a/changelog.d/19829.doc +++ /dev/null @@ -1 +0,0 @@ -Tweak wording of Rust crate dependency update policy. \ No newline at end of file diff --git a/changelog.d/19832.misc b/changelog.d/19832.misc deleted file mode 100644 index 23c63fa10c..0000000000 --- a/changelog.d/19832.misc +++ /dev/null @@ -1 +0,0 @@ -Fix a flaky test (`twisted.protocols.amp.TooLong` error under `trial -jN`) caused by an oversized debug log line. diff --git a/changelog.d/19834.bugfix b/changelog.d/19834.bugfix deleted file mode 100644 index 097a09bac6..0000000000 --- a/changelog.d/19834.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix notification counts being inflated after a `/purge_history` when notifications had already been rotated into the summary table. diff --git a/changelog.d/19840.misc b/changelog.d/19840.misc deleted file mode 100644 index db56b65764..0000000000 --- a/changelog.d/19840.misc +++ /dev/null @@ -1 +0,0 @@ -Upload Complement test logs as CI artifacts instead of printing the raw output to the build log. diff --git a/changelog.d/19843.misc b/changelog.d/19843.misc deleted file mode 100644 index 633c11edb7..0000000000 --- a/changelog.d/19843.misc +++ /dev/null @@ -1 +0,0 @@ -Fix release script considering any workflow completion as successful. \ No newline at end of file diff --git a/changelog.d/19845.bugfix b/changelog.d/19845.bugfix deleted file mode 100644 index c2009f0749..0000000000 --- a/changelog.d/19845.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix `/sync` caching transient errors for the `sync_response_cache_duration`. \ No newline at end of file diff --git a/changelog.d/19847.doc b/changelog.d/19847.doc deleted file mode 100644 index 225b03d7b3..0000000000 --- a/changelog.d/19847.doc +++ /dev/null @@ -1 +0,0 @@ -Fixed the Admin API user endpoint documentation examples to use JSON booleans (true/false) instead of numeric (0/1) values. diff --git a/changelog.d/19848.feature b/changelog.d/19848.feature deleted file mode 100644 index a7b891e547..0000000000 --- a/changelog.d/19848.feature +++ /dev/null @@ -1 +0,0 @@ -Add new metric `synapse_non_deactivated_user_count` which tracks the number of non-deactivated users in the database, split by `app_service`. diff --git a/changelog.d/19849.misc b/changelog.d/19849.misc deleted file mode 100644 index 08a3bb0e96..0000000000 --- a/changelog.d/19849.misc +++ /dev/null @@ -1 +0,0 @@ -Force keyword-args for clear `default_config(server_name="test")` usage in test utilities. diff --git a/changelog.d/19850.bugfix b/changelog.d/19850.bugfix deleted file mode 100644 index 357c2ee3f3..0000000000 --- a/changelog.d/19850.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix local events being deleted by the [Purge History admin API](https://element-hq.github.io/synapse/v1.155/admin_api/purge_history_api.html) despite `delete_local_events` being set to false, in room versions other than 1 and 2. \ No newline at end of file diff --git a/changelog.d/19853.feature b/changelog.d/19853.feature deleted file mode 100644 index b11eaa16d7..0000000000 --- a/changelog.d/19853.feature +++ /dev/null @@ -1,3 +0,0 @@ -The `GET /_matrix/client/unstable/org.matrix.msc1763/retention/configuration` endpoint is now provided when retention -is enabled and `experimental_features.msc1763_enabled` is enabled, based on -[MSC1763](https://github.com/matrix-org/matrix-spec-proposals/pull/1763). diff --git a/changelog.d/19854.misc b/changelog.d/19854.misc deleted file mode 100644 index 501745ebb1..0000000000 --- a/changelog.d/19854.misc +++ /dev/null @@ -1 +0,0 @@ -Add `.ruff_cache/` directory to `.gitignore`. diff --git a/changelog.d/19855.bugfix b/changelog.d/19855.bugfix deleted file mode 100644 index 706f779174..0000000000 --- a/changelog.d/19855.bugfix +++ /dev/null @@ -1 +0,0 @@ -Provide remote servers a way to find out about an event created during the remote join handshake. Contributed by @FrenchGithubUser and @jason-famedly @ Famedly. diff --git a/changelog.d/19856.bugfix b/changelog.d/19856.bugfix deleted file mode 100644 index 706f779174..0000000000 --- a/changelog.d/19856.bugfix +++ /dev/null @@ -1 +0,0 @@ -Provide remote servers a way to find out about an event created during the remote join handshake. Contributed by @FrenchGithubUser and @jason-famedly @ Famedly. diff --git a/changelog.d/19866.misc b/changelog.d/19866.misc deleted file mode 100644 index 7bf704e84c..0000000000 --- a/changelog.d/19866.misc +++ /dev/null @@ -1 +0,0 @@ -Bump `poetry` in CI from `2.2.1` to `2.4.1`. \ No newline at end of file diff --git a/changelog.d/19868.misc b/changelog.d/19868.misc deleted file mode 100644 index cd182ea15c..0000000000 --- a/changelog.d/19868.misc +++ /dev/null @@ -1 +0,0 @@ -Split out `deferred` and `tokio_runtime` to their own Rust modules. diff --git a/changelog.d/19869.bugfix b/changelog.d/19869.bugfix deleted file mode 100644 index 2772db850f..0000000000 --- a/changelog.d/19869.bugfix +++ /dev/null @@ -1 +0,0 @@ -Make simple_select_one_onecol_txn() more helpful by naming the table of the select - as all other query wrapper functions already did. diff --git a/changelog.d/19874.feature b/changelog.d/19874.feature deleted file mode 100644 index 57238e820f..0000000000 --- a/changelog.d/19874.feature +++ /dev/null @@ -1 +0,0 @@ -Add experimental support for [MSC4491: Invite reasons in room creation](https://github.com/matrix-org/matrix-spec-proposals/pull/4491). \ No newline at end of file diff --git a/changelog.d/19877.misc b/changelog.d/19877.misc deleted file mode 100644 index 7bf704e84c..0000000000 --- a/changelog.d/19877.misc +++ /dev/null @@ -1 +0,0 @@ -Bump `poetry` in CI from `2.2.1` to `2.4.1`. \ No newline at end of file diff --git a/changelog.d/19883.misc b/changelog.d/19883.misc deleted file mode 100644 index 673e8cea1a..0000000000 --- a/changelog.d/19883.misc +++ /dev/null @@ -1 +0,0 @@ -Prevent the `cargo-deny` and `cargo-bench` CI jobs from being skipped, even on PRs that have Rust changes. \ No newline at end of file diff --git a/changelog.d/19892.bugfix b/changelog.d/19892.bugfix deleted file mode 100644 index 9439433274..0000000000 --- a/changelog.d/19892.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix a bug where a user's dehydrated device (MSC3814) was deleted when their device list was synced from MAS (e.g. on logging out their last device), breaking offline key delivery. diff --git a/debian/changelog b/debian/changelog index 224916d2f6..a60203b68b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,9 @@ -matrix-synapse-py3 (1.155.0+nmu1) UNRELEASED; urgency=medium +matrix-synapse-py3 (1.156.0~rc1) stable; urgency=medium * Bump poetry from 2.2.1 to 2.4.1. + * New Synapse release 1.156.0rc1. - -- Synapse Packaging team Wed, 24 Jun 2026 13:10:00 +0100 + -- Synapse Packaging team Tue, 30 Jun 2026 14:37:46 +0100 matrix-synapse-py3 (1.155.0) stable; urgency=medium diff --git a/pyproject.toml b/pyproject.toml index 316850bd22..1e31b23d8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "matrix-synapse" -version = "1.155.0" +version = "1.156.0rc1" description = "Homeserver for the Matrix decentralised comms protocol" readme = "README.rst" authors = [ diff --git a/schema/synapse-config.schema.yaml b/schema/synapse-config.schema.yaml index f58b923add..0e345b7b69 100644 --- a/schema/synapse-config.schema.yaml +++ b/schema/synapse-config.schema.yaml @@ -1,5 +1,5 @@ $schema: https://element-hq.github.io/synapse/latest/schema/v1/meta.schema.json -$id: https://element-hq.github.io/synapse/schema/synapse/v1.155/synapse-config.schema.json +$id: https://element-hq.github.io/synapse/schema/synapse/v1.156/synapse-config.schema.json type: object properties: modules: From fd8f0e53a374c9757acbb349bc791832429728ff Mon Sep 17 00:00:00 2001 From: Olivier 'reivilibre Date: Tue, 30 Jun 2026 16:42:21 +0100 Subject: [PATCH 4/5] Tweak changelog --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 58a061bd43..10d8dcc928 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -42,7 +42,7 @@ - Add `.ruff_cache/` directory to `.gitignore`. ([\#19854](https://github.com/element-hq/synapse/issues/19854)) - Bump `poetry` in CI from `2.2.1` to `2.4.1`. ([\#19866](https://github.com/element-hq/synapse/issues/19866), [\#19877](https://github.com/element-hq/synapse/issues/19877)) - Split out `deferred` and `tokio_runtime` to their own Rust modules. ([\#19868](https://github.com/element-hq/synapse/issues/19868)) -- Prevent the `cargo-deny` and `cargo-bench` CI jobs from being skipped, even on PRs that have Rust changes. ([\#19883](https://github.com/element-hq/synapse/issues/19883)) +- Prevent the `cargo-test` and `cargo-bench` CI jobs from being skipped, even on PRs that have Rust changes. ([\#19883](https://github.com/element-hq/synapse/issues/19883)) # Synapse 1.155.0 (2026-06-16) From aa976873056b1fccb4d3a63f2b2c915440304fac Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Tue, 30 Jun 2026 22:20:26 +0100 Subject: [PATCH 5/5] Change MSC3814 dehydrated device `/events` endpoint from POST to GET (#19896) Change `/org.matrix.msc3814.v1/dehydrated_device/[device_id]/events` to accept GET requests instead of POST. The original version of [MSC3814](https://github.com/matrix-org/matrix-spec-proposals/pull/3814) said we should delete keys after returning them from this endpoint, but it is being updated to say we should not delete them, and therefore the appropriate verb is GET. Synapse already doesn't delete anything, so we just need to change to a GET with a `next_batch` query param. (Currently it is a POST with `next_batch` in the JSON content.) This code was initially written by @ara4n and Claude, but both he and I have read it and think it makes sense. I am far from a Synapse expert, so feel free to tell me it's all wrong and point me in the right direction. I don't know what system tests will be affected by this, but I guess we will see when the CI runs (right?). This is a change to an unstable endpoint so no need for notifications about breaking changes or similar. Part of https://github.com/element-hq/element-meta/issues/2704 ### Pull Request Checklist * [x] Pull request is based on the develop branch * [x] Pull request includes a [changelog file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog). The entry should: * [x] [Code style](https://element-hq.github.io/synapse/latest/code_style.html) is correct (run the [linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters)) --------- Co-authored-by: Matthew Hodgson --- changelog.d/19896.misc | 1 + synapse/rest/client/devices.py | 33 +++++++++++++++++++++++++++++++ tests/rest/client/test_devices.py | 12 ++++------- 3 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 changelog.d/19896.misc diff --git a/changelog.d/19896.misc b/changelog.d/19896.misc new file mode 100644 index 0000000000..2c6a8740ef --- /dev/null +++ b/changelog.d/19896.misc @@ -0,0 +1 @@ +Change the [MSC3814](https://github.com/matrix-org/matrix-spec-proposals/pull/3814) dehydrated device `/events` endpoint from `POST` to `GET`. diff --git a/synapse/rest/client/devices.py b/synapse/rest/client/devices.py index 0231ed374d..76043e6120 100644 --- a/synapse/rest/client/devices.py +++ b/synapse/rest/client/devices.py @@ -33,6 +33,7 @@ from synapse.http.servlet import ( RestServlet, parse_and_validate_json_object_from_request, parse_integer, + parse_string, ) from synapse.http.site import SynapseRequest from synapse.rest.client._base import client_patterns, interactive_auth_handler @@ -249,17 +250,49 @@ class DehydratedDeviceEventsServlet(RestServlet): self.auth = hs.get_auth() self.store = hs.get_datastores().main + async def on_GET( + self, request: SynapseRequest, device_id: str + ) -> tuple[int, JsonDict]: + requester = await self.auth.get_user_by_req(request) + + next_batch = parse_string(request, "next_batch") + limit = parse_integer(request, "limit", 100) + + msgs = await self.message_handler.get_events_for_dehydrated_device( + requester=requester, + device_id=device_id, + since_token=next_batch, + limit=limit, + ) + + return 200, msgs + class PostBody(RequestBodyModel): + """ + This is deprecated: you should use GET instead. + + The POST version is provided temporarily for backwards compatibility + with a previous unstable draft of MSC3814. + """ + next_batch: StrictStr | None = None async def on_POST( self, request: SynapseRequest, device_id: str ) -> tuple[int, JsonDict]: + """ + This is deprecated: you should use GET instead. + + The POST version is provided temporarily for backwards compatibility + with a previous unstable draft of MSC3814. + """ + requester = await self.auth.get_user_by_req(request) next_batch = parse_and_validate_json_object_from_request( request, self.PostBody ).next_batch + limit = parse_integer(request, "limit", 100) msgs = await self.message_handler.get_events_for_dehydrated_device( diff --git a/tests/rest/client/test_devices.py b/tests/rest/client/test_devices.py index 2cf293a962..bdf9ad1786 100644 --- a/tests/rest/client/test_devices.py +++ b/tests/rest/client/test_devices.py @@ -224,9 +224,8 @@ class DehydratedDeviceTestCase(unittest.HomeserverTestCase): # make sure we can fetch the message with our dehydrated device id channel = self.make_request( - "POST", + "GET", f"_matrix/client/unstable/org.matrix.msc3814.v1/dehydrated_device/{device_id}/events", - content={}, access_token=token, shorthand=False, ) @@ -236,9 +235,8 @@ class DehydratedDeviceTestCase(unittest.HomeserverTestCase): # fetch messages again and make sure that the message was not deleted channel = self.make_request( - "POST", + "GET", f"_matrix/client/unstable/org.matrix.msc3814.v1/dehydrated_device/{device_id}/events", - content={}, access_token=token, shorthand=False, ) @@ -248,11 +246,9 @@ class DehydratedDeviceTestCase(unittest.HomeserverTestCase): # make sure fetching messages with next batch token works - there are no unfetched # messages so we should receive an empty array - content = {"next_batch": next_batch_token} channel = self.make_request( - "POST", - f"_matrix/client/unstable/org.matrix.msc3814.v1/dehydrated_device/{device_id}/events", - content=content, + "GET", + f"_matrix/client/unstable/org.matrix.msc3814.v1/dehydrated_device/{device_id}/events?next_batch={next_batch_token}", access_token=token, shorthand=False, )