Repository navigation
[Server Announcements] [Create/Configure Rooms] Configure announcement rooms and reconcile them on startup - #20372
Conversation
Schema DiffPlease check that this looks as expected! common (sqlite)Unchanged common (postgres)Unchanged main (sqlite)--- /tmp/schema_diff_xrxysdcb/before/main/full_schemas/9999/full.sql.sqlite 2026-10-26 16:43:57.103430723 +0000
+++ /tmp/schema_diff_xrxysdcb/after/main/full_schemas/9999/full.sql.sqlite 2026-10-26 16:43:40.296500333 +0000
@@ -1219,20 +1219,27 @@
-- all subsequent sticky events for the same room with higher
-- `stream_id`s are also unsent.
--
-- Not a foreign key because we must still support expiration of sticky events.
sticky_events_stream_position INTEGER NOT NULL,
-- It's enough to track one position per (destination, room_id) pair.
PRIMARY KEY (destination, room_id)
);
CREATE INDEX destination_rooms_sticky_events_backlog_room_id ON destination_rooms_sticky_events_backlog (room_id);
+CREATE TABLE server_announcement_rooms (
+ config_id TEXT PRIMARY KEY NOT NULL,
+ room_id TEXT UNIQUE NOT NULL,
+ avatar_path TEXT,
+ avatar_sha256 TEXT,
+ avatar_mxc TEXT
+);
CREATE UNIQUE INDEX receipts_graph_unique_index ON receipts_graph (room_id, receipt_type, user_id) WHERE thread_id IS NULL;
CREATE UNIQUE INDEX receipts_linearized_unique_index ON receipts_linearized (room_id, receipt_type, user_id) WHERE thread_id IS NULL;
CREATE INDEX device_lists_changes_in_room_by_room_idx ON device_lists_changes_in_room (room_id, stream_id) ;
CREATE INDEX events_jump_to_date_idx ON events (room_id, origin_server_ts) WHERE NOT outlier;
CREATE INDEX room_membership_user_room_idx ON room_memberships (user_id, room_id) ;
CREATE UNIQUE INDEX profiles_full_user_id_key ON profiles (full_user_id) ;
CREATE UNIQUE INDEX full_users_unique_idx ON user_filters (full_user_id, filter_id) ;
CREATE INDEX event_push_actions_stream_highlight_index ON event_push_actions (highlight, stream_ordering) WHERE highlight=0;
CREATE INDEX current_state_events_stream_ordering_idx ON current_state_events (event_stream_ordering) ;
CREATE INDEX local_current_membership_stream_ordering_idx ON local_current_membership (event_stream_ordering) ;
main (postgres)--- /tmp/schema_diff_xrxysdcb/before/main/full_schemas/9999/full.sql.postgres 2026-10-26 16:43:57.910426927 +0000
+++ /tmp/schema_diff_xrxysdcb/after/main/full_schemas/9999/full.sql.postgres 2026-10-26 16:43:41.275494764 +0000
@@ -1022,20 +1022,27 @@
CREATE TABLE scheduled_tasks (
id text NOT NULL,
action text NOT NULL,
status text NOT NULL,
"timestamp" bigint NOT NULL,
resource_id text,
params text,
result text,
error text
);
+CREATE TABLE server_announcement_rooms (
+ config_id text NOT NULL,
+ room_id text NOT NULL,
+ avatar_path text,
+ avatar_sha256 text,
+ avatar_mxc text
+);
CREATE TABLE server_keys_json (
server_name text NOT NULL,
key_id text NOT NULL,
from_server text NOT NULL,
ts_added_ms bigint NOT NULL,
ts_valid_until_ms bigint NOT NULL,
key_json bytea NOT NULL
);
CREATE TABLE server_signature_keys (
server_name text,
@@ -1572,20 +1579,24 @@
ALTER TABLE ONLY room_stats_current
ADD CONSTRAINT room_stats_current_pkey PRIMARY KEY (room_id);
ALTER TABLE ONLY room_tags_revisions
ADD CONSTRAINT room_tag_revisions_uniqueness UNIQUE (user_id, room_id);
ALTER TABLE ONLY room_tags
ADD CONSTRAINT room_tag_uniqueness UNIQUE (user_id, room_id, tag);
ALTER TABLE ONLY rooms
ADD CONSTRAINT rooms_pkey PRIMARY KEY (room_id);
ALTER TABLE ONLY scheduled_tasks
ADD CONSTRAINT scheduled_tasks_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY server_announcement_rooms
+ ADD CONSTRAINT server_announcement_rooms_pkey PRIMARY KEY (config_id);
+ALTER TABLE ONLY server_announcement_rooms
+ ADD CONSTRAINT server_announcement_rooms_room_id_key UNIQUE (room_id);
ALTER TABLE ONLY server_keys_json
ADD CONSTRAINT server_keys_json_uniqueness UNIQUE (server_name, key_id, from_server);
ALTER TABLE ONLY server_signature_keys
ADD CONSTRAINT server_signature_keys_server_name_key_id_key UNIQUE (server_name, key_id);
ALTER TABLE ONLY sessions
ADD CONSTRAINT sessions_session_type_session_id_key UNIQUE (session_type, session_id);
ALTER TABLE ONLY sliding_sync_connection_positions
ADD CONSTRAINT sliding_sync_connection_positions_pkey PRIMARY KEY (connection_position);
ALTER TABLE ONLY sliding_sync_connection_required_state
ADD CONSTRAINT sliding_sync_connection_required_state_pkey PRIMARY KEY (required_state_id);
state (sqlite)Unchanged state (postgres)Unchanged |
8190e6f to
e638bf5
Compare
e638bf5 to
9a307d4
Compare
Allow storing mappings between server announcement room config and configured Matrix rooms.
9a307d4 to
326f828
Compare
Create and update the room state of server announcement rooms that are configured. Rooms that are removed from configuration are kept, but no longer given special treatment. To remove old announcement rooms, server admins can purge them in the usual manner. Purging these rooms also removes the announcement `id` -> matrix room ID mapping in the `server_announcement_rooms` table.
326f828 to
ffdc35f
Compare
| None. | ||
| """ | ||
|
|
||
| async def get_server_announcement_room(self, announcement_id: str) -> str | None: |
There was a problem hiding this comment.
nit: because this is only internal naming
I found it confusing when reviewing that the "Announcement Room Id" was announcement_id. I understand we cannot use room_id either.
Maybe something like config_id would make things less confusing
There was a problem hiding this comment.
Good point. I also struggled with naming the concept. I like config_id!
Updated in 0601d67.
| # We don't want announcement room events to federate to other homeservers. | ||
| # If this room is somehow federating, then we shouldn't use it. | ||
| raise ConfigError( | ||
| f'Announcement room {room.id} ({room_id}) has "m.federate": true ' | ||
| "in its create event. Please create another room (with a new id)", | ||
| ("experimental_features", "server_announcements", "rooms", room.id), |
There was a problem hiding this comment.
🥲 not sure how this can happen, but the safeguard is nice
There was a problem hiding this comment.
Only if you manually created it, I suppose.
But yes, it's mainly preventative.
| # Check that all configured announcement sending users exist. | ||
| for user_id in room.allowed_user_ids: | ||
| user = await self._store.get_user_by_id(user_id) | ||
| if user is None or user.is_deactivated: |
There was a problem hiding this comment.
Seems reasonable to handle deactivated account.
I wonder if we should just warn instead of raising an error in that case.
Being forced to reconfigure after the account is de-activated could create some friction between user management and ops.
There was a problem hiding this comment.
Good point, that would be an annoying failure mode.
Updated in c24bf7a.
To prevent user deactivation from preventing the homeserver from starting up.
as suggested by Paul. Much better term.
Co-authored-by: Paul Chobert <paul@chobert.fr>
for consistency's sake.
…ver_announcements_config_rooms
…t rooms and reconcile them on startup (element-hq#20372) Co-authored-by: Paul Chobert <paul@chobert.fr>
> [!NOTE] > **Epic**: [Efficient Server Announcements](#20240) > Follows: #20372 When a new announcement is sent in the announcement room, this PR will ensure that the announcement and the room state are shared with the client over Legacy Sync. If you reviewed the PoC before, please note that there are several differences: - We intentionally don't wake all sync - We don't want typing and receipts - We don't need device lists ### Pull Request Checklist <!-- Please read https://element-hq.github.io/synapse/latest/development/contributing_guide.html before submitting your pull request --> * [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: - Be a short description of your change which makes sense to users. "Fixed a bug that prevented receiving messages from other servers." instead of "Moved X method from `EventStore` to `EventWorkerStore`.". - Use markdown where necessary, mostly for `code blocks`. - End with either a period (.) or an exclamation mark (!). - Start with a capital letter. - Feel free to credit yourself, by adding a sentence "Contributed by @github_username." or "Contributed by [Your Name]." to the end of the entry. * [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: Quentin Gliech <quenting@element.io>
Follow-up to #20372, and re-uses the changelog. Adds a new config option to exclude a given list of users from being proactively pushed events from a given announcement room. Requested by the ESS team at #20344 (comment) ### Pull Request Checklist <!-- Please read https://element-hq.github.io/synapse/latest/development/contributing_guide.html before submitting your pull request --> * [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: - Be a short description of your change which makes sense to users. "Fixed a bug that prevented receiving messages from other servers." instead of "Moved X method from `EventStore` to `EventWorkerStore`.". - Use markdown where necessary, mostly for `code blocks`. - End with either a period (.) or an exclamation mark (!). - Start with a capital letter. - Feel free to credit yourself, by adding a sentence "Contributed by @github_username." or "Contributed by [Your Name]." to the end of the entry. * [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: Quentin Gliech <quenting@element.io>
> [!NOTE] > Epic: [Efficient Server Announcements](#20240) > Follows: #20372 This PR first tests that every user can read the server announcements through the "history" endpoints like - [x] `GET /rooms/{roomId}/messages` - [x] `GET /rooms/{roomId}/context/{eventId}` - [x] `GET /rooms/{roomId}/event/{eventId}` - [x] `GET /rooms/{roomId}/relations/{eventId}` - [x] `GET /rooms/{roomId}/threads` - [x] `GET /rooms/{roomId}/initialSync` This already works for users who have never been in the room, because announcement rooms are world-readable. It doesn't for a sender who was removed from the room's config: Synapse still uses their `leave` membership and shows them the room as it was when they left. This PR then treats a user who left a configured announcement room like a user who has never been in it. ### Pull Request Checklist <!-- Please read https://element-hq.github.io/synapse/latest/development/contributing_guide.html before submitting your pull request --> * [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: - Be a short description of your change which makes sense to users. "Fixed a bug that prevented receiving messages from other servers." instead of "Moved X method from `EventStore` to `EventWorkerStore`.". - Use markdown where necessary, mostly for `code blocks`. - End with either a period (.) or an exclamation mark (!). - Start with a capital letter. - Feel free to credit yourself, by adding a sentence "Contributed by @github_username." or "Contributed by [Your Name]." to the end of the entry. * [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: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Co-authored-by: Quentin Gliech <quenting@element.io>
Epic ticket: #20240
Part 1 of implementing #20344
This PR implements configuring server announcement rooms. Server admins can configure one or more announcement rooms and decide who is allowed to send events in it. The homeserver will then actually create the room, and configure it. The reasoning behind the choices in state events are in #20344.
Preventing room members from modifying state will come in a follow-up PR.
Recommended to review commit-by-commit.
Pull Request Checklist
EventStoretoEventWorkerStore.".code blocks.