From 849032d6d540c316e8112e5d4fae228404a4bd87 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 13 Jul 2026 12:00:12 +0100 Subject: [PATCH 1/4] Add default push rules for live location share start (MSCxxxx draft) Underride rules .io.element.rule.beacon_info(_one_to_one) matching org.matrix.msc3672.beacon_info with content.live == true, so recipients are pushed when someone starts a live location share (incl. E2EE rooms, since beacon_info is an unencrypted state event). Gated behind experimental_features.mscxxxx_beacon_push_rules_enabled; rule ids and flag to be renamed once the MSC is numbered. Co-Authored-By: Claude Fable 5 --- rust/src/push/base_rules.rs | 37 +++++++++++++++++++++ rust/src/push/evaluator.rs | 1 + rust/src/push/mod.rs | 9 +++++ synapse/config/experimental.py | 7 ++++ synapse/storage/databases/main/push_rule.py | 1 + synapse/synapse_rust/push.pyi | 1 + 6 files changed, 56 insertions(+) diff --git a/rust/src/push/base_rules.rs b/rust/src/push/base_rules.rs index 47d5289006b..7472d65bda0 100644 --- a/rust/src/push/base_rules.rs +++ b/rust/src/push/base_rules.rs @@ -721,6 +721,43 @@ pub const BASE_APPEND_UNDERRIDE_RULES: &[PushRule] = &[ default: true, default_enabled: true, }, + PushRule { + rule_id: Cow::Borrowed("global/underride/.io.element.rule.beacon_info_one_to_one"), + priority_class: 1, + conditions: Cow::Borrowed(&[ + Condition::Known(KnownCondition::RoomMemberCount { + is: Some(Cow::Borrowed("2")), + }), + Condition::Known(KnownCondition::EventMatch(EventMatchCondition { + key: Cow::Borrowed("type"), + pattern: Cow::Borrowed("org.matrix.msc3672.beacon_info"), + })), + Condition::Known(KnownCondition::EventPropertyIs(EventPropertyIsCondition { + key: Cow::Borrowed("content.live"), + value: Cow::Owned(SimpleJsonValue::Bool(true)), + })), + ]), + actions: Cow::Borrowed(&[Action::Notify, SOUND_ACTION]), + default: true, + default_enabled: true, + }, + PushRule { + rule_id: Cow::Borrowed("global/underride/.io.element.rule.beacon_info"), + priority_class: 1, + conditions: Cow::Borrowed(&[ + Condition::Known(KnownCondition::EventMatch(EventMatchCondition { + key: Cow::Borrowed("type"), + pattern: Cow::Borrowed("org.matrix.msc3672.beacon_info"), + })), + Condition::Known(KnownCondition::EventPropertyIs(EventPropertyIsCondition { + key: Cow::Borrowed("content.live"), + value: Cow::Owned(SimpleJsonValue::Bool(true)), + })), + ]), + actions: Cow::Borrowed(&[Action::Notify]), + default: true, + default_enabled: true, + }, ]; lazy_static! { diff --git a/rust/src/push/evaluator.rs b/rust/src/push/evaluator.rs index 1cbca4c6355..250d2890c09 100644 --- a/rust/src/push/evaluator.rs +++ b/rust/src/push/evaluator.rs @@ -637,6 +637,7 @@ fn test_requires_room_version_supports_condition() { false, false, false, + false, ), None, None, diff --git a/rust/src/push/mod.rs b/rust/src/push/mod.rs index 780d7a8cbd8..31f35907dd4 100644 --- a/rust/src/push/mod.rs +++ b/rust/src/push/mod.rs @@ -559,6 +559,7 @@ pub struct FilteredPushRules { msc4028_push_encrypted_events: bool, msc4210_enabled: bool, msc4306_enabled: bool, + mscxxxx_beacon_push_rules_enabled: bool, } #[pymethods] @@ -574,6 +575,7 @@ impl FilteredPushRules { msc4028_push_encrypted_events: bool, msc4210_enabled: bool, msc4306_enabled: bool, + mscxxxx_beacon_push_rules_enabled: bool, ) -> Self { Self { push_rules, @@ -584,6 +586,7 @@ impl FilteredPushRules { msc4028_push_encrypted_events, msc4210_enabled, msc4306_enabled, + mscxxxx_beacon_push_rules_enabled, } } @@ -620,6 +623,12 @@ impl FilteredPushRules { return false; } + if !self.mscxxxx_beacon_push_rules_enabled + && rule.rule_id.contains("/.io.element.rule.beacon_info") + { + return false; + } + if !self.msc4028_push_encrypted_events && rule.rule_id == "global/override/.org.matrix.msc4028.encrypted_event" { diff --git a/synapse/config/experimental.py b/synapse/config/experimental.py index f99f7b139ec..c58324589bd 100644 --- a/synapse/config/experimental.py +++ b/synapse/config/experimental.py @@ -156,6 +156,13 @@ def read_config( "msc3381_polls_enabled", False ) + # MSCXXXX (unnumbered draft): default push rules for live location share + # beacon_info start events (the MSC3672 state event). Unstable rule ids: + # .io.element.rule.beacon_info(_one_to_one). + self.mscxxxx_beacon_push_rules_enabled: bool = experimental.get( + "mscxxxx_beacon_push_rules_enabled", False + ) + # MSC3912: Relation-based redactions. self.msc3912_enabled: bool = experimental.get("msc3912_enabled", False) diff --git a/synapse/storage/databases/main/push_rule.py b/synapse/storage/databases/main/push_rule.py index d361166cec4..5e5eb6ddec1 100644 --- a/synapse/storage/databases/main/push_rule.py +++ b/synapse/storage/databases/main/push_rule.py @@ -107,6 +107,7 @@ def _load_rules( msc4028_push_encrypted_events=experimental_config.msc4028_push_encrypted_events, msc4210_enabled=experimental_config.msc4210_enabled, msc4306_enabled=experimental_config.msc4306_enabled, + mscxxxx_beacon_push_rules_enabled=experimental_config.mscxxxx_beacon_push_rules_enabled, ) return filtered_rules diff --git a/synapse/synapse_rust/push.pyi b/synapse/synapse_rust/push.pyi index ef0d5f94f4f..bac316c38b0 100644 --- a/synapse/synapse_rust/push.pyi +++ b/synapse/synapse_rust/push.pyi @@ -50,6 +50,7 @@ class FilteredPushRules: msc4028_push_encrypted_events: bool, msc4210_enabled: bool, msc4306_enabled: bool, + mscxxxx_beacon_push_rules_enabled: bool, ): ... def rules(self) -> Collection[tuple[PushRule, bool]]: ... From 6e37f382b2190e9d6cefcc215594f657d7256e30 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 14 Jul 2026 12:50:08 +0100 Subject: [PATCH 2/4] Adopt MSC4505 identifiers for the live location share push rules The MSC has been published as MSC4505 (Push Rules for Live Location Sharing): rename the experimental flag to msc4505_enabled and the rule ids to the unstable identifiers it defines (.org.matrix.msc4505.rule.beacon_info(_one_to_one)), and add the .org.matrix.msc4505.rule.beacon override it also defines, explicitly suppressing beacon update events in unencrypted rooms. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Uk8aPxHn3BHCe52L226jdG --- rust/src/push/base_rules.rs | 20 ++++++++++++++++++-- rust/src/push/mod.rs | 10 +++++----- synapse/config/experimental.py | 11 +++++------ synapse/storage/databases/main/push_rule.py | 2 +- synapse/synapse_rust/push.pyi | 2 +- 5 files changed, 30 insertions(+), 15 deletions(-) diff --git a/rust/src/push/base_rules.rs b/rust/src/push/base_rules.rs index 7472d65bda0..c71e57b0be9 100644 --- a/rust/src/push/base_rules.rs +++ b/rust/src/push/base_rules.rs @@ -273,6 +273,22 @@ pub const BASE_APPEND_OVERRIDE_RULES: &[PushRule] = &[ default: true, default_enabled: true, }, + // MSC4505: explicitly suppress live location share beacon updates in unencrypted + // rooms (in encrypted rooms they arrive as m.room.encrypted and can only be + // suppressed client-side after decryption). + PushRule { + rule_id: Cow::Borrowed("global/override/.org.matrix.msc4505.rule.beacon"), + priority_class: 5, + conditions: Cow::Borrowed(&[Condition::Known(KnownCondition::EventMatch( + EventMatchCondition { + key: Cow::Borrowed("type"), + pattern: Cow::Borrowed("org.matrix.msc3672.beacon"), + }, + ))]), + actions: Cow::Borrowed(&[]), + default: true, + default_enabled: true, + }, ]; pub const BASE_APPEND_CONTENT_RULES: &[PushRule] = &[PushRule { @@ -722,7 +738,7 @@ pub const BASE_APPEND_UNDERRIDE_RULES: &[PushRule] = &[ default_enabled: true, }, PushRule { - rule_id: Cow::Borrowed("global/underride/.io.element.rule.beacon_info_one_to_one"), + rule_id: Cow::Borrowed("global/underride/.org.matrix.msc4505.rule.beacon_info_one_to_one"), priority_class: 1, conditions: Cow::Borrowed(&[ Condition::Known(KnownCondition::RoomMemberCount { @@ -742,7 +758,7 @@ pub const BASE_APPEND_UNDERRIDE_RULES: &[PushRule] = &[ default_enabled: true, }, PushRule { - rule_id: Cow::Borrowed("global/underride/.io.element.rule.beacon_info"), + rule_id: Cow::Borrowed("global/underride/.org.matrix.msc4505.rule.beacon_info"), priority_class: 1, conditions: Cow::Borrowed(&[ Condition::Known(KnownCondition::EventMatch(EventMatchCondition { diff --git a/rust/src/push/mod.rs b/rust/src/push/mod.rs index 31f35907dd4..2854dd6e889 100644 --- a/rust/src/push/mod.rs +++ b/rust/src/push/mod.rs @@ -559,7 +559,7 @@ pub struct FilteredPushRules { msc4028_push_encrypted_events: bool, msc4210_enabled: bool, msc4306_enabled: bool, - mscxxxx_beacon_push_rules_enabled: bool, + msc4505_enabled: bool, } #[pymethods] @@ -575,7 +575,7 @@ impl FilteredPushRules { msc4028_push_encrypted_events: bool, msc4210_enabled: bool, msc4306_enabled: bool, - mscxxxx_beacon_push_rules_enabled: bool, + msc4505_enabled: bool, ) -> Self { Self { push_rules, @@ -586,7 +586,7 @@ impl FilteredPushRules { msc4028_push_encrypted_events, msc4210_enabled, msc4306_enabled, - mscxxxx_beacon_push_rules_enabled, + msc4505_enabled, } } @@ -623,8 +623,8 @@ impl FilteredPushRules { return false; } - if !self.mscxxxx_beacon_push_rules_enabled - && rule.rule_id.contains("/.io.element.rule.beacon_info") + if !self.msc4505_enabled + && rule.rule_id.contains("/.org.matrix.msc4505.rule.beacon") { return false; } diff --git a/synapse/config/experimental.py b/synapse/config/experimental.py index c58324589bd..c1c83100676 100644 --- a/synapse/config/experimental.py +++ b/synapse/config/experimental.py @@ -156,12 +156,11 @@ def read_config( "msc3381_polls_enabled", False ) - # MSCXXXX (unnumbered draft): default push rules for live location share - # beacon_info start events (the MSC3672 state event). Unstable rule ids: - # .io.element.rule.beacon_info(_one_to_one). - self.mscxxxx_beacon_push_rules_enabled: bool = experimental.get( - "mscxxxx_beacon_push_rules_enabled", False - ) + # MSC4505: default push rules for live location sharing — notify on + # beacon_info start events (the MSC3672 state event), suppress beacon + # updates. Unstable rule ids: .org.matrix.msc4505.rule.beacon_info + # (_one_to_one) / .org.matrix.msc4505.rule.beacon. + self.msc4505_enabled: bool = experimental.get("msc4505_enabled", False) # MSC3912: Relation-based redactions. self.msc3912_enabled: bool = experimental.get("msc3912_enabled", False) diff --git a/synapse/storage/databases/main/push_rule.py b/synapse/storage/databases/main/push_rule.py index 5e5eb6ddec1..6aa1baa5a64 100644 --- a/synapse/storage/databases/main/push_rule.py +++ b/synapse/storage/databases/main/push_rule.py @@ -107,7 +107,7 @@ def _load_rules( msc4028_push_encrypted_events=experimental_config.msc4028_push_encrypted_events, msc4210_enabled=experimental_config.msc4210_enabled, msc4306_enabled=experimental_config.msc4306_enabled, - mscxxxx_beacon_push_rules_enabled=experimental_config.mscxxxx_beacon_push_rules_enabled, + msc4505_enabled=experimental_config.msc4505_enabled, ) return filtered_rules diff --git a/synapse/synapse_rust/push.pyi b/synapse/synapse_rust/push.pyi index bac316c38b0..8799d08a151 100644 --- a/synapse/synapse_rust/push.pyi +++ b/synapse/synapse_rust/push.pyi @@ -50,7 +50,7 @@ class FilteredPushRules: msc4028_push_encrypted_events: bool, msc4210_enabled: bool, msc4306_enabled: bool, - mscxxxx_beacon_push_rules_enabled: bool, + msc4505_enabled: bool, ): ... def rules(self) -> Collection[tuple[PushRule, bool]]: ... From 3ad7748e82fe93f20f4009808b56e23d3a005f4f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 14 Jul 2026 13:52:49 +0100 Subject: [PATCH 3/4] Add changelog entry --- changelog.d/19954.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/19954.feature diff --git a/changelog.d/19954.feature b/changelog.d/19954.feature new file mode 100644 index 00000000000..7a87e557663 --- /dev/null +++ b/changelog.d/19954.feature @@ -0,0 +1 @@ +Add experimental support for [MSC4505](https://github.com/matrix-org/matrix-spec-proposals/pull/4505): push rules for live location sharing. From 8f1482389d40f35320e38596487f8746ba782efe Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 14 Jul 2026 14:03:58 +0100 Subject: [PATCH 4/4] Fix the evaluator benchmark for the new FilteredPushRules argument Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Uk8aPxHn3BHCe52L226jdG --- rust/benches/evaluator.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/benches/evaluator.rs b/rust/benches/evaluator.rs index 96169fd45d9..e59a6d2f6ab 100644 --- a/rust/benches/evaluator.rs +++ b/rust/benches/evaluator.rs @@ -215,6 +215,7 @@ fn bench_eval_message(b: &mut Bencher) { false, false, false, + false, ); b.iter(|| eval.run(&rules, Some("bob"), Some("person"), None));