Skip to content

[Server Announcements] [Create/Configure Rooms] Configure announcement rooms and reconcile them on startup - #20372

Merged
anoadragon453 merged 8 commits into
developfrom
anoa/server_announcements_config_rooms
Oct 26, 2026
Merged

anoadragon453 merged 8 commits into
developfrom
anoa/server_announcements_config_rooms

Conversation

@anoadragon453

@anoadragon453 anoadragon453 commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

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

  • Pull request is based on the develop branch
  • Pull request includes a changelog file. 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.
  • Code style is correct (run the linters)

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Schema Diff

Please 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

@anoadragon453
anoadragon453 force-pushed the anoa/server_announcements_config_rooms branch 3 times, most recently from 8190e6f to e638bf5 Compare October 16, 2026 20:48
@anoadragon453
anoadragon453 force-pushed the anoa/server_announcements_config_rooms branch from e638bf5 to 9a307d4 Compare October 16, 2026 21:00
Allow storing mappings between server announcement room config and
configured Matrix rooms.
@anoadragon453
anoadragon453 force-pushed the anoa/server_announcements_config_rooms branch from 9a307d4 to 326f828 Compare October 19, 2026 10:55
@anoadragon453
anoadragon453 marked this pull request as ready for review October 19, 2026 10:55
@anoadragon453
anoadragon453 requested a review from a team as a code owner October 19, 2026 10:55
@anoadragon453
anoadragon453 requested review from barodeur and sandhose and removed request for a team and sandhose October 19, 2026 10:55
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.
@anoadragon453
anoadragon453 force-pushed the anoa/server_announcements_config_rooms branch from 326f828 to ffdc35f Compare October 19, 2026 11:24
@MadLittleMods MadLittleMods added the A-Server-Announcements Message all users on a homeserver at once (separate from "Server Notices") label Oct 19, 2026

@barodeur barodeur left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🔥

None.
"""

async def get_server_announcement_room(self, announcement_id: str) -> str | None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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

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.

Good point. I also struggled with naming the concept. I like config_id!

Updated in 0601d67.

Comment on lines +171 to +176
# 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),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🥲 not sure how this can happen, but the safeguard is nice

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.

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:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

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.

Good point, that would be an annoying failure mode.

Updated in c24bf7a.

anoadragon453 and others added 5 commits October 26, 2026 14:54
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.
@anoadragon453
anoadragon453 merged commit b276152 into develop Oct 26, 2026
48 checks passed
@anoadragon453
anoadragon453 deleted the anoa/server_announcements_config_rooms branch October 26, 2026 17:20
sakaru pushed a commit to sakaru/synapse that referenced this pull request Oct 28, 2026
…t rooms and reconcile them on startup (element-hq#20372)

Co-authored-by: Paul Chobert <paul@chobert.fr>
sandhose added a commit that referenced this pull request Nov 3, 2026
> [!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>
sandhose added a commit that referenced this pull request Nov 3, 2026
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>
sandhose added a commit that referenced this pull request Nov 3, 2026
> [!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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Server-Announcements Message all users on a homeserver at once (separate from "Server Notices")

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants