From 9a00d019266c919cb52bcec99a0af9ae53bb054d Mon Sep 17 00:00:00 2001 From: Bart Oostveen Date: Fri, 10 Jul 2026 01:13:11 +0200 Subject: [PATCH 01/16] nixos/tests/matrix-continuwuity: refactor to mautrix library This is because [matrix-nio](https://github.com/matrix-nio/matrix-nio) is pretty dead, and does not seem to like the new continuwuity version with the new default v12 rooms. Mautrix is actively maintained and a good fit for basic testing of the client-server connection on NixOS. (cherry picked from commit fe3454e664e7c550ad7eecbd51a34cc0b0195f0c) --- nixos/tests/matrix/continuwuity.nix | 69 ++++++++++++++++------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/nixos/tests/matrix/continuwuity.nix b/nixos/tests/matrix/continuwuity.nix index cf07293b1ab0..91571301661a 100644 --- a/nixos/tests/matrix/continuwuity.nix +++ b/nixos/tests/matrix/continuwuity.nix @@ -20,61 +20,66 @@ in }; networking.firewall.allowedTCPPorts = [ 6167 ]; }; + client = { pkgs, ... }: { environment.systemPackages = [ - (pkgs.writers.writePython3Bin "do_test" { libraries = [ pkgs.python3Packages.matrix-nio ]; } '' + (pkgs.writers.writePython3Bin "do_test" { libraries = [ pkgs.python3Packages.mautrix ]; } '' import asyncio - import nio + + from mautrix.client import Client + from mautrix.types import EventType, RoomFilter async def main() -> None: # Connect to continuwuity - client = nio.AsyncClient("http://continuwuity:6167", "${user}") + client = Client( + mxid="@${user}:${name}", + base_url="http://continuwuity:6167", + ) # Log in as user alice - response = await client.login("${pass}") + await client.login(password="${pass}") # Create a new room - response = await client.room_create(federate=False) - print("Matrix room create response:", response) - assert isinstance(response, nio.RoomCreateResponse) - room_id = response.room_id + room_id = await client.create_room() + print("Created room:", room_id) # Join the room - response = await client.join(room_id) - print("Matrix join response:", response) - assert isinstance(response, nio.JoinResponse) + await client.join_room_by_id(room_id) + print("Joined room") # Send a message to the room - response = await client.room_send( - room_id=room_id, - message_type="m.room.message", - content={ - "msgtype": "m.text", - "body": "Hello continuwuity!" - } - ) - print("Matrix room send response:", response) - assert isinstance(response, nio.RoomSendResponse) + received = asyncio.Event() + msg = "Hello continuwuity!" - # Sync responses - response = await client.sync(timeout=30000) - print("Matrix sync response:", response) - assert isinstance(response, nio.SyncResponse) + async def on_message(evt): + if ( + evt.room_id != room_id + or evt.sender != client.mxid + or evt.type != EventType.ROOM_MESSAGE + ): + return - # Check the message was received by continuwuity - last_message = response.rooms.join[room_id].timeline.events[-1].body - assert last_message == "Hello continuwuity!" + assert evt.content.body == msg + received.set() + + client.add_event_handler(EventType.ROOM_MESSAGE, on_message) + sync_task = client.start(RoomFilter(rooms=[room_id])) + + await client.send_text(room_id, msg) + + # Sync until message is received + await asyncio.wait_for(received.wait(), timeout=30) # Leave the room - response = await client.room_leave(room_id) - print("Matrix room leave response:", response) - assert isinstance(response, nio.RoomLeaveResponse) + await client.leave_room(room_id) + print("Left room") # Close the client - await client.close() + client.stop() + await sync_task if __name__ == "__main__": From 0afe6913a8909bd19220ab8f6819ebb157f421f6 Mon Sep 17 00:00:00 2001 From: Bart Oostveen Date: Fri, 10 Jul 2026 01:37:53 +0200 Subject: [PATCH 02/16] nixos/tests/matrix-continuwuity: add bartoostveen as a maintainer (cherry picked from commit 91bc748a73e6b14f74e69af75c6701c664314029) --- nixos/tests/matrix/continuwuity.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nixos/tests/matrix/continuwuity.nix b/nixos/tests/matrix/continuwuity.nix index 91571301661a..80c46fb21362 100644 --- a/nixos/tests/matrix/continuwuity.nix +++ b/nixos/tests/matrix/continuwuity.nix @@ -101,6 +101,7 @@ in ''; meta.maintainers = with lib.maintainers; [ + bartoostveen nyabinary snaki ]; From 08dee96fff2d4e8e7462b7ba8a86f8459e6fe44f Mon Sep 17 00:00:00 2001 From: Bart Oostveen Date: Mon, 28 Sep 2026 09:42:53 +0200 Subject: [PATCH 03/16] matrix-continuwuity_latest: init from matrix-continuwuity, matrix-continuwuity: mark as vulnerable Not going for an override here because this is cleaner, as to avoid more divergence from master. Not-cherry-picked-because: avoiding breaking changes on master for security backport --- .../ma/matrix-continuwuity/package.nix | 13 + .../0001-fix-backport-SEC10.patch | 250 ++++++++++++++++++ .../0002-fix-backport-SEC28.patch | 37 +++ .../0003-fix-backport-SEC26.patch | 132 +++++++++ .../ma/matrix-continuwuity_latest/package.nix | 104 ++++++++ 5 files changed, 536 insertions(+) create mode 100644 pkgs/by-name/ma/matrix-continuwuity_latest/0001-fix-backport-SEC10.patch create mode 100644 pkgs/by-name/ma/matrix-continuwuity_latest/0002-fix-backport-SEC28.patch create mode 100644 pkgs/by-name/ma/matrix-continuwuity_latest/0003-fix-backport-SEC26.patch create mode 100644 pkgs/by-name/ma/matrix-continuwuity_latest/package.nix diff --git a/pkgs/by-name/ma/matrix-continuwuity/package.nix b/pkgs/by-name/ma/matrix-continuwuity/package.nix index bb1bd56c34b8..f1b055b21d6a 100644 --- a/pkgs/by-name/ma/matrix-continuwuity/package.nix +++ b/pkgs/by-name/ma/matrix-continuwuity/package.nix @@ -100,5 +100,18 @@ rustPlatform.buildRustPackage (finalAttrs: { ]; # Not a typo, continuwuity is a drop-in replacement for conduwuit. mainProgram = "conduwuit"; + knownVulnerabilities = [ + '' + Continuwuity 0.5.10 can no longer be securely ran, as it contains a critical security-related bug that is currently embargoed at the time of writing. + Continuwuity internally tracks this as SEC8, see also: https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v26.9.1 + + The latest version of Continuwuity has been moved to `matrix-continuwuity_latest` as to not introduce breaking changes. + It is highly advisable to upgrade to this version as fast as possible. When deploying Continuwuity using the NixOS module, you may do so using: + + `services.matrix-continuwuity.package = pkgs.matrix-continuwuity_latest`. + + The most notable breaking change of the 26.x release is the removal of LDAP. + '' + ]; }; }) diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/0001-fix-backport-SEC10.patch b/pkgs/by-name/ma/matrix-continuwuity_latest/0001-fix-backport-SEC10.patch new file mode 100644 index 000000000000..042b4b12aaf2 --- /dev/null +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/0001-fix-backport-SEC10.patch @@ -0,0 +1,250 @@ +From d25c12c343b53ea90025c11808700ad6267f4e59 Mon Sep 17 00:00:00 2001 +From: timedout +Date: Wed, 29 Jul 2026 16:38:05 +0100 +Subject: [PATCH] fix(backport): SEC10 + +Reviewed-By: Ginger +Co-Authored-By: Erwan Leboucher + +(cherry picked from commit 71016a0d7f79289f3bf8d7816c1fec3c85c43153) +--- + src/api/client/sync/v5.rs | 133 +++++++++++++++++++++++++++++++------- + 1 file changed, 111 insertions(+), 22 deletions(-) + +diff --git a/src/api/client/sync/v5.rs b/src/api/client/sync/v5.rs +index 183f3aaac..03a4c1972 100644 +--- a/src/api/client/sync/v5.rs ++++ b/src/api/client/sync/v5.rs +@@ -1,6 +1,6 @@ + use std::{ + cmp::{self, Ordering}, +- collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque}, ++ collections::{BTreeMap, BTreeSet, HashMap, HashSet}, + ops::Deref, + time::Duration, + }; +@@ -28,6 +28,7 @@ + use ruma::{ + DeviceId, OwnedEventId, OwnedRoomId, RoomId, UInt, UserId, + api::client::sync::sync_events::{self, DeviceLists, UnreadNotificationsCount}, ++ assign, + directory::RoomTypeFilter, + events::{ + AnyRawAccountDataEvent, AnySyncEphemeralRoomEvent, AnySyncStateEvent, StateEventType, +@@ -139,6 +140,13 @@ pub(crate) async fn sync_events_v5_route( + let (all_joined_rooms, all_invited_rooms, all_knocked_rooms) = + join3(all_joined_rooms, all_invited_rooms, all_knocked_rooms).await; + ++ let allowed_rooms: BTreeSet = all_joined_rooms ++ .iter() ++ .chain(all_invited_rooms.iter()) ++ .chain(all_knocked_rooms.iter()) ++ .cloned() ++ .collect(); ++ + let all_joined_rooms = all_joined_rooms.iter().map(AsRef::as_ref); + let all_invited_rooms = all_invited_rooms.iter().map(AsRef::as_ref); + let all_knocked_rooms = all_knocked_rooms.iter().map(AsRef::as_ref); +@@ -192,13 +200,14 @@ pub(crate) async fn sync_events_v5_route( + ) + .await; + +- fetch_subscriptions(services, sync_info, &known_rooms, &mut todo_rooms).await; ++ fetch_subscriptions(services, sync_info, &known_rooms, &allowed_rooms, &mut todo_rooms).await; + + response.rooms = process_rooms( + services, + sender_user, + next_batch, + all_invited_rooms.clone(), ++ all_knocked_rooms.clone(), + &todo_rooms, + &mut response, + &body, +@@ -208,6 +217,7 @@ pub(crate) async fn sync_events_v5_route( + if response.rooms.iter().all(|(id, r)| { + r.timeline.is_empty() + && r.required_state.is_empty() ++ && r.invite_state.is_none() + && !response.extensions.receipts.rooms.contains_key(id) + }) && response + .extensions +@@ -238,10 +248,17 @@ async fn fetch_subscriptions( + services: &Services, + (sender_user, sender_device, globalsince, body): SyncInfo<'_>, + known_rooms: &KnownRooms, ++ allowed_rooms: &BTreeSet, + todo_rooms: &mut TodoRooms, + ) { + let mut known_subscription_rooms = BTreeSet::new(); + for (room_id, room) in &body.room_subscriptions { ++ // Silently ignore subscriptions to rooms the user is not a member of ++ // (joined or invited). ++ if !allowed_rooms.contains(room_id) { ++ continue; ++ } ++ + let not_exists = services.rooms.metadata.exists(room_id).eq(&false); + + let is_disabled = services.rooms.metadata.is_disabled(room_id); +@@ -399,11 +416,13 @@ async fn handle_lists<'a, Rooms, AllRooms>( + BTreeMap::default() + } + ++#[allow(clippy::too_many_arguments)] + async fn process_rooms<'a, Rooms>( + services: &Services, + sender_user: &UserId, + next_batch: u64, + all_invited_rooms: Rooms, ++ all_knocked_rooms: Rooms, + todo_rooms: &TodoRooms, + response: &mut sync_events::v5::Response, + body: &sync_events::v5::Request, +@@ -416,38 +435,99 @@ async fn process_rooms<'a, Rooms>( + let roomsincecount = PduCount::Normal(*roomsince); + + let mut timestamp: Option<_> = None; +- let mut invite_state = None; + let (timeline_pdus, limited); + let new_room_id: &RoomId = (*room_id).as_ref(); + if all_invited_rooms.clone().any(is_equal_to!(new_room_id)) { ++ let Ok(invite_count) = services ++ .rooms ++ .state_cache ++ .get_invite_count(room_id, sender_user) ++ .await ++ else { ++ continue; ++ }; ++ ++ if *roomsince >= invite_count { ++ continue; ++ } ++ + // TODO: figure out a timestamp we can use for remote invites +- invite_state = services ++ let invite_state = services + .rooms + .state_cache + .invite_state(sender_user, room_id) + .await + .ok(); + +- (timeline_pdus, limited) = (VecDeque::new(), true); +- } else { +- TimelinePdus { pdus: timeline_pdus, limited } = match load_timeline( +- services, +- sender_user, +- room_id, +- Some(roomsincecount), +- Some(PduCount::from(next_batch)), +- *timeline_limit, +- ) +- .await +- { +- | Ok(value) => value, +- | Err(err) => { +- warn!("Encountered missing timeline in {}, error {}", room_id, err); +- continue; +- }, ++ rooms.insert(room_id.clone(), sync_events::v5::response::Room { ++ initial: Some(roomsince == &0), ++ invite_state, ++ limited: true, ++ ..Default::default() ++ }); ++ continue; ++ } ++ ++ if all_knocked_rooms.clone().any(is_equal_to!(new_room_id)) { ++ let Ok(knock_count) = services ++ .rooms ++ .state_cache ++ .get_knock_count(room_id, sender_user) ++ .await ++ else { ++ continue; + }; ++ ++ if *roomsince >= knock_count { ++ continue; ++ } ++ ++ let Ok(knock_state) = services ++ .rooms ++ .state_cache ++ .knock_state(sender_user, room_id) ++ .await ++ else { ++ continue; ++ }; ++ ++ rooms.insert( ++ room_id.clone(), ++ assign!(sync_events::v5::response::Room::new(), { ++ initial: Some(roomsince == &0), ++ invite_state: Some(knock_state), ++ limited: true, ++ }), ++ ); ++ continue; ++ } ++ ++ if !services ++ .rooms ++ .state_cache ++ .is_joined(sender_user, room_id) ++ .await ++ { ++ continue; + } + ++ TimelinePdus { pdus: timeline_pdus, limited } = match load_timeline( ++ services, ++ sender_user, ++ room_id, ++ Some(roomsincecount), ++ Some(PduCount::from(next_batch)), ++ *timeline_limit, ++ ) ++ .await ++ { ++ | Ok(value) => value, ++ | Err(err) => { ++ warn!("Encountered missing timeline in {}, error {}", room_id, err); ++ continue; ++ }, ++ }; ++ + if body.extensions.account_data.enabled == Some(true) { + response.extensions.account_data.rooms.insert( + room_id.to_owned(), +@@ -627,7 +707,7 @@ async fn process_rooms<'a, Rooms>( + }, + initial: Some(roomsince == &0), + is_dm: None, +- invite_state, ++ invite_state: None, + unread_notifications: UnreadNotificationsCount { + highlight_count: Some( + services +@@ -753,6 +833,15 @@ async fn collect_typing_events( + + let mut typing_response = sync_events::v5::response::Typing::default(); + for (room_id, (_, _, roomsince)) in todo_rooms { ++ if !services ++ .rooms ++ .state_cache ++ .is_joined(sender_user, room_id) ++ .await ++ { ++ continue; ++ } ++ + if services.rooms.typing.last_typing_update(room_id).await? <= *roomsince { + continue; + } +-- +2.55.0 + diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/0002-fix-backport-SEC28.patch b/pkgs/by-name/ma/matrix-continuwuity_latest/0002-fix-backport-SEC28.patch new file mode 100644 index 000000000000..d07419844698 --- /dev/null +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/0002-fix-backport-SEC28.patch @@ -0,0 +1,37 @@ +From dcb079931a929880518015794d709f93651154ca Mon Sep 17 00:00:00 2001 +From: Ginger +Date: Tue, 11 Aug 2026 15:59:26 -0400 +Subject: [PATCH 1/2] fix(backport): SEC28 + +(cherry picked from commit 49a8f6f53b6f86ed6abb8952843cb3a38c530ada) +--- + src/service/threepid/mod.rs | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/service/threepid/mod.rs b/src/service/threepid/mod.rs +index dcc0b58ab..bda80f82e 100644 +--- a/src/service/threepid/mod.rs ++++ b/src/service/threepid/mod.rs +@@ -112,6 +112,10 @@ pub async fn send_validation_email( + // If a validation session already exists for this client secret, we can either + // reuse it with a new token or return early because it's already valid. + | Some(session) => { ++ if session.email != recipient.email { ++ return Err!(Request(InvalidParam("Wrong email for session."))); ++ } ++ + match session.validation_state { + | ValidationState::Validated => { + // If the existing session is already valid, don't send an email. +@@ -119,7 +123,7 @@ pub async fn send_validation_email( + }, + | ValidationState::Pending(ref mut token) => { + // Check ratelimiting for the target address. +- if self.ratelimiter.check_key(&recipient.email).is_err() { ++ if self.ratelimiter.check_key(&session.email).is_err() { + return Err(Error::BadRequest( + ErrorKind::LimitExceeded { retry_after: None }, + "You're sending emails too fast, try again in a few minutes.", +-- +2.55.0 + diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/0003-fix-backport-SEC26.patch b/pkgs/by-name/ma/matrix-continuwuity_latest/0003-fix-backport-SEC26.patch new file mode 100644 index 000000000000..8736fdbeea72 --- /dev/null +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/0003-fix-backport-SEC26.patch @@ -0,0 +1,132 @@ +From a00a615799bc55bc093a6298735732565aa2b566 Mon Sep 17 00:00:00 2001 +From: Ginger +Date: Tue, 11 Aug 2026 16:35:24 -0400 +Subject: [PATCH 2/2] fix(backport): SEC26 + +Reviewed-By: timedout +Reviewed-By: Ginger +(cherry picked from commit 700fbe472d4a8385b96f870256bfc00f9a15f809) +--- + src/api/server/event_auth.rs | 27 ++++++++++----------------- + src/api/server/state.rs | 13 ++++++++++++- + src/api/server/state_ids.rs | 13 ++++++++++++- + 3 files changed, 34 insertions(+), 19 deletions(-) + +diff --git a/src/api/server/event_auth.rs b/src/api/server/event_auth.rs +index a9019e8e7..433e18f46 100644 +--- a/src/api/server/event_auth.rs ++++ b/src/api/server/event_auth.rs +@@ -1,12 +1,9 @@ + use std::{borrow::Borrow, iter::once}; + + use axum::extract::State; +-use conduwuit::{Err, Error, Result, info, utils::stream::ReadyExt}; ++use conduwuit::{Err, Result, Event, info, utils::stream::ReadyExt}; + use futures::StreamExt; +-use ruma::{ +- RoomId, +- api::{client::error::ErrorKind, federation::authorization::get_event_authorization}, +-}; ++use ruma::api::federation::authorization::get_event_authorization; + + use super::AccessCheck; + use crate::Ruma; +@@ -42,25 +39,21 @@ pub(crate) async fn get_event_authorization_route( + return Err!(Request(NotFound("This server is not participating in that room."))); + } + +- let event = services ++ // The event must be in the room we just authorised access to ++ if !services + .rooms + .timeline +- .get_pdu_json(&body.event_id) ++ .get_pdu(&body.event_id) + .await +- .map_err(|_| Error::BadRequest(ErrorKind::NotFound, "Event not found."))?; +- +- let room_id_str = event +- .get("room_id") +- .and_then(|val| val.as_str()) +- .ok_or_else(|| Error::bad_database("Invalid event in database."))?; +- +- let room_id = <&RoomId>::try_from(room_id_str) +- .map_err(|_| Error::bad_database("Invalid room_id in event in database."))?; ++ .is_ok_and(|pdu| pdu.room_id_or_hash() == body.room_id) ++ { ++ return Err!(Request(NotFound("Event not found."))); ++ } + + let auth_chain = services + .rooms + .auth_chain +- .event_ids_iter(room_id, once(body.event_id.borrow())) ++ .event_ids_iter(&body.room_id, once(body.event_id.borrow())) + .ready_filter_map(Result::ok) + .filter_map(|id| async move { services.rooms.timeline.get_pdu_json(&id).await.ok() }) + .then(|pdu| services.sending.convert_to_outgoing_federation_event(pdu)) +diff --git a/src/api/server/state.rs b/src/api/server/state.rs +index 5e1ad8ca2..05a008b74 100644 +--- a/src/api/server/state.rs ++++ b/src/api/server/state.rs +@@ -1,7 +1,7 @@ + use std::{borrow::Borrow, iter::once}; + + use axum::extract::State; +-use conduwuit::{Err, Result, at, err, info, utils::IterStream}; ++use conduwuit::{Err, Event, Result, at, err, info, utils::IterStream}; + use futures::{FutureExt, StreamExt, TryStreamExt}; + use ruma::{OwnedEventId, api::federation::event::get_room_state}; + +@@ -24,6 +24,17 @@ pub(crate) async fn get_room_state_route( + .check() + .await?; + ++ // The event must be in the room we just authorised access to ++ if !services ++ .rooms ++ .timeline ++ .get_pdu(&body.event_id) ++ .await ++ .is_ok_and(|pdu| pdu.room_id_or_hash() == body.room_id) ++ { ++ return Err!(Request(NotFound("Event not found."))); ++ } ++ + if !services + .rooms + .state_cache +diff --git a/src/api/server/state_ids.rs b/src/api/server/state_ids.rs +index c9dea3116..206f3efc5 100644 +--- a/src/api/server/state_ids.rs ++++ b/src/api/server/state_ids.rs +@@ -1,7 +1,7 @@ + use std::{borrow::Borrow, iter::once}; + + use axum::extract::State; +-use conduwuit::{Err, Result, at, err, info}; ++use conduwuit::{Err, Event, Result, at, err, info}; + use futures::{StreamExt, TryStreamExt}; + use ruma::{OwnedEventId, api::federation::event::get_room_state_ids}; + +@@ -25,6 +25,17 @@ pub(crate) async fn get_room_state_ids_route( + .check() + .await?; + ++ // The event must be in the room we just authorised access to ++ if !services ++ .rooms ++ .timeline ++ .get_pdu(&body.event_id) ++ .await ++ .is_ok_and(|pdu| pdu.room_id_or_hash() == body.room_id) ++ { ++ return Err!(Request(NotFound("Event not found."))); ++ } ++ + if !services + .rooms + .state_cache +-- +2.55.0 + diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix new file mode 100644 index 000000000000..c94e635f7543 --- /dev/null +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix @@ -0,0 +1,104 @@ +{ + lib, + rustPlatform, + fetchFromGitea, + pkg-config, + bzip2, + zstd, + stdenv, + rocksdb, + nix-update-script, + testers, + matrix-continuwuity_latest, + rust-jemalloc-sys-unprefixed, + liburing, + nixosTests, +}: +let + rocksdb' = + (rocksdb.override { + # rocksdb does not support prefixed jemalloc, which is required on darwin + enableJemalloc = !stdenv.hostPlatform.isDarwin; + jemalloc = rust-jemalloc-sys-unprefixed; + }).overrideAttrs + ( + final: old: { + version = "10.10.1"; + src = fetchFromGitea { + domain = "forgejo.ellis.link"; + owner = "continuwuation"; + repo = "rocksdb"; + rev = "10.10.fb"; + hash = "sha256-1ef75IDMs5Hba4VWEyXPJb02JyShy5k4gJfzGDhopRk="; + }; + + patches = [ ]; + } + ); +in +rustPlatform.buildRustPackage (finalAttrs: { + pname = "matrix-continuwuity"; + version = "0.5.10"; + + src = fetchFromGitea { + domain = "forgejo.ellis.link"; + owner = "continuwuation"; + repo = "continuwuity"; + tag = "v${finalAttrs.version}"; + hash = "sha256-oevEGYlAK/rMJhm200CkwerT5oVak8sJj0Fa6r6+J/Q="; + }; + + cargoHash = "sha256-uvMiFURXxkLbbbwq4pG5hevsLZHQ1wVfTNvzQRTQWxE="; + + patches = [ + ./0001-fix-backport-SEC10.patch + ./0002-fix-backport-SEC28.patch + ./0003-fix-backport-SEC26.patch + ]; + + nativeBuildInputs = [ + pkg-config + rustPlatform.bindgenHook + ]; + + buildInputs = [ + bzip2 + zstd + rust-jemalloc-sys-unprefixed + liburing + ]; + + env = { + ZSTD_SYS_USE_PKG_CONFIG = true; + ROCKSDB_INCLUDE_DIR = "${rocksdb'}/include"; + ROCKSDB_LIB_DIR = "${rocksdb'}/lib"; + }; + + passthru = { + rocksdb = rocksdb'; # make used rocksdb version available (e.g., for backup scripts) + updateScript = nix-update-script { }; + tests = { + version = testers.testVersion { + inherit (finalAttrs) version; + package = matrix-continuwuity_latest; + }; + } + // lib.optionalAttrs stdenv.hostPlatform.isLinux { + inherit (nixosTests) matrix-continuwuity; + }; + }; + + meta = { + description = "Matrix homeserver written in Rust, forked from conduwuit"; + homepage = "https://continuwuity.org/"; + changelog = "https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v${finalAttrs.version}"; + license = lib.licenses.asl20; + maintainers = with lib.maintainers; [ + bartoostveen + nyabinary + snaki + ]; + # Not a typo, continuwuity is a drop-in replacement for conduwuit. + mainProgram = "conduwuit"; + }; +}) From ef1a8f9dea8140628beadb9e77f8de6cb2c2dee5 Mon Sep 17 00:00:00 2001 From: Bart Oostveen Date: Mon, 28 Sep 2026 09:53:41 +0200 Subject: [PATCH 04/16] matrix-continuwuity_latest: drop backports We are propagating breaking changes of 26.x (removal of LDAP support, along with a few minor changed) to stable, because of multiple security fixes that are not feasible to backport to 0.5.10. This is because that would require rebasing ruwuma (the Continuwuity fork that is not maintained since 26.x) on top of ruma, as well as backporting the thousands of lines of code that came with the ruwuma -> ruma migration. Not-cherry-picked-because: neccesary for backporting all changes on master to stable, see commit message. --- .../0001-fix-backport-SEC10.patch | 250 ------------------ .../0002-fix-backport-SEC28.patch | 37 --- .../0003-fix-backport-SEC26.patch | 132 --------- .../ma/matrix-continuwuity_latest/package.nix | 6 - 4 files changed, 425 deletions(-) delete mode 100644 pkgs/by-name/ma/matrix-continuwuity_latest/0001-fix-backport-SEC10.patch delete mode 100644 pkgs/by-name/ma/matrix-continuwuity_latest/0002-fix-backport-SEC28.patch delete mode 100644 pkgs/by-name/ma/matrix-continuwuity_latest/0003-fix-backport-SEC26.patch diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/0001-fix-backport-SEC10.patch b/pkgs/by-name/ma/matrix-continuwuity_latest/0001-fix-backport-SEC10.patch deleted file mode 100644 index 042b4b12aaf2..000000000000 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/0001-fix-backport-SEC10.patch +++ /dev/null @@ -1,250 +0,0 @@ -From d25c12c343b53ea90025c11808700ad6267f4e59 Mon Sep 17 00:00:00 2001 -From: timedout -Date: Wed, 29 Jul 2026 16:38:05 +0100 -Subject: [PATCH] fix(backport): SEC10 - -Reviewed-By: Ginger -Co-Authored-By: Erwan Leboucher - -(cherry picked from commit 71016a0d7f79289f3bf8d7816c1fec3c85c43153) ---- - src/api/client/sync/v5.rs | 133 +++++++++++++++++++++++++++++++------- - 1 file changed, 111 insertions(+), 22 deletions(-) - -diff --git a/src/api/client/sync/v5.rs b/src/api/client/sync/v5.rs -index 183f3aaac..03a4c1972 100644 ---- a/src/api/client/sync/v5.rs -+++ b/src/api/client/sync/v5.rs -@@ -1,6 +1,6 @@ - use std::{ - cmp::{self, Ordering}, -- collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque}, -+ collections::{BTreeMap, BTreeSet, HashMap, HashSet}, - ops::Deref, - time::Duration, - }; -@@ -28,6 +28,7 @@ - use ruma::{ - DeviceId, OwnedEventId, OwnedRoomId, RoomId, UInt, UserId, - api::client::sync::sync_events::{self, DeviceLists, UnreadNotificationsCount}, -+ assign, - directory::RoomTypeFilter, - events::{ - AnyRawAccountDataEvent, AnySyncEphemeralRoomEvent, AnySyncStateEvent, StateEventType, -@@ -139,6 +140,13 @@ pub(crate) async fn sync_events_v5_route( - let (all_joined_rooms, all_invited_rooms, all_knocked_rooms) = - join3(all_joined_rooms, all_invited_rooms, all_knocked_rooms).await; - -+ let allowed_rooms: BTreeSet = all_joined_rooms -+ .iter() -+ .chain(all_invited_rooms.iter()) -+ .chain(all_knocked_rooms.iter()) -+ .cloned() -+ .collect(); -+ - let all_joined_rooms = all_joined_rooms.iter().map(AsRef::as_ref); - let all_invited_rooms = all_invited_rooms.iter().map(AsRef::as_ref); - let all_knocked_rooms = all_knocked_rooms.iter().map(AsRef::as_ref); -@@ -192,13 +200,14 @@ pub(crate) async fn sync_events_v5_route( - ) - .await; - -- fetch_subscriptions(services, sync_info, &known_rooms, &mut todo_rooms).await; -+ fetch_subscriptions(services, sync_info, &known_rooms, &allowed_rooms, &mut todo_rooms).await; - - response.rooms = process_rooms( - services, - sender_user, - next_batch, - all_invited_rooms.clone(), -+ all_knocked_rooms.clone(), - &todo_rooms, - &mut response, - &body, -@@ -208,6 +217,7 @@ pub(crate) async fn sync_events_v5_route( - if response.rooms.iter().all(|(id, r)| { - r.timeline.is_empty() - && r.required_state.is_empty() -+ && r.invite_state.is_none() - && !response.extensions.receipts.rooms.contains_key(id) - }) && response - .extensions -@@ -238,10 +248,17 @@ async fn fetch_subscriptions( - services: &Services, - (sender_user, sender_device, globalsince, body): SyncInfo<'_>, - known_rooms: &KnownRooms, -+ allowed_rooms: &BTreeSet, - todo_rooms: &mut TodoRooms, - ) { - let mut known_subscription_rooms = BTreeSet::new(); - for (room_id, room) in &body.room_subscriptions { -+ // Silently ignore subscriptions to rooms the user is not a member of -+ // (joined or invited). -+ if !allowed_rooms.contains(room_id) { -+ continue; -+ } -+ - let not_exists = services.rooms.metadata.exists(room_id).eq(&false); - - let is_disabled = services.rooms.metadata.is_disabled(room_id); -@@ -399,11 +416,13 @@ async fn handle_lists<'a, Rooms, AllRooms>( - BTreeMap::default() - } - -+#[allow(clippy::too_many_arguments)] - async fn process_rooms<'a, Rooms>( - services: &Services, - sender_user: &UserId, - next_batch: u64, - all_invited_rooms: Rooms, -+ all_knocked_rooms: Rooms, - todo_rooms: &TodoRooms, - response: &mut sync_events::v5::Response, - body: &sync_events::v5::Request, -@@ -416,38 +435,99 @@ async fn process_rooms<'a, Rooms>( - let roomsincecount = PduCount::Normal(*roomsince); - - let mut timestamp: Option<_> = None; -- let mut invite_state = None; - let (timeline_pdus, limited); - let new_room_id: &RoomId = (*room_id).as_ref(); - if all_invited_rooms.clone().any(is_equal_to!(new_room_id)) { -+ let Ok(invite_count) = services -+ .rooms -+ .state_cache -+ .get_invite_count(room_id, sender_user) -+ .await -+ else { -+ continue; -+ }; -+ -+ if *roomsince >= invite_count { -+ continue; -+ } -+ - // TODO: figure out a timestamp we can use for remote invites -- invite_state = services -+ let invite_state = services - .rooms - .state_cache - .invite_state(sender_user, room_id) - .await - .ok(); - -- (timeline_pdus, limited) = (VecDeque::new(), true); -- } else { -- TimelinePdus { pdus: timeline_pdus, limited } = match load_timeline( -- services, -- sender_user, -- room_id, -- Some(roomsincecount), -- Some(PduCount::from(next_batch)), -- *timeline_limit, -- ) -- .await -- { -- | Ok(value) => value, -- | Err(err) => { -- warn!("Encountered missing timeline in {}, error {}", room_id, err); -- continue; -- }, -+ rooms.insert(room_id.clone(), sync_events::v5::response::Room { -+ initial: Some(roomsince == &0), -+ invite_state, -+ limited: true, -+ ..Default::default() -+ }); -+ continue; -+ } -+ -+ if all_knocked_rooms.clone().any(is_equal_to!(new_room_id)) { -+ let Ok(knock_count) = services -+ .rooms -+ .state_cache -+ .get_knock_count(room_id, sender_user) -+ .await -+ else { -+ continue; - }; -+ -+ if *roomsince >= knock_count { -+ continue; -+ } -+ -+ let Ok(knock_state) = services -+ .rooms -+ .state_cache -+ .knock_state(sender_user, room_id) -+ .await -+ else { -+ continue; -+ }; -+ -+ rooms.insert( -+ room_id.clone(), -+ assign!(sync_events::v5::response::Room::new(), { -+ initial: Some(roomsince == &0), -+ invite_state: Some(knock_state), -+ limited: true, -+ }), -+ ); -+ continue; -+ } -+ -+ if !services -+ .rooms -+ .state_cache -+ .is_joined(sender_user, room_id) -+ .await -+ { -+ continue; - } - -+ TimelinePdus { pdus: timeline_pdus, limited } = match load_timeline( -+ services, -+ sender_user, -+ room_id, -+ Some(roomsincecount), -+ Some(PduCount::from(next_batch)), -+ *timeline_limit, -+ ) -+ .await -+ { -+ | Ok(value) => value, -+ | Err(err) => { -+ warn!("Encountered missing timeline in {}, error {}", room_id, err); -+ continue; -+ }, -+ }; -+ - if body.extensions.account_data.enabled == Some(true) { - response.extensions.account_data.rooms.insert( - room_id.to_owned(), -@@ -627,7 +707,7 @@ async fn process_rooms<'a, Rooms>( - }, - initial: Some(roomsince == &0), - is_dm: None, -- invite_state, -+ invite_state: None, - unread_notifications: UnreadNotificationsCount { - highlight_count: Some( - services -@@ -753,6 +833,15 @@ async fn collect_typing_events( - - let mut typing_response = sync_events::v5::response::Typing::default(); - for (room_id, (_, _, roomsince)) in todo_rooms { -+ if !services -+ .rooms -+ .state_cache -+ .is_joined(sender_user, room_id) -+ .await -+ { -+ continue; -+ } -+ - if services.rooms.typing.last_typing_update(room_id).await? <= *roomsince { - continue; - } --- -2.55.0 - diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/0002-fix-backport-SEC28.patch b/pkgs/by-name/ma/matrix-continuwuity_latest/0002-fix-backport-SEC28.patch deleted file mode 100644 index d07419844698..000000000000 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/0002-fix-backport-SEC28.patch +++ /dev/null @@ -1,37 +0,0 @@ -From dcb079931a929880518015794d709f93651154ca Mon Sep 17 00:00:00 2001 -From: Ginger -Date: Tue, 11 Aug 2026 15:59:26 -0400 -Subject: [PATCH 1/2] fix(backport): SEC28 - -(cherry picked from commit 49a8f6f53b6f86ed6abb8952843cb3a38c530ada) ---- - src/service/threepid/mod.rs | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/src/service/threepid/mod.rs b/src/service/threepid/mod.rs -index dcc0b58ab..bda80f82e 100644 ---- a/src/service/threepid/mod.rs -+++ b/src/service/threepid/mod.rs -@@ -112,6 +112,10 @@ pub async fn send_validation_email( - // If a validation session already exists for this client secret, we can either - // reuse it with a new token or return early because it's already valid. - | Some(session) => { -+ if session.email != recipient.email { -+ return Err!(Request(InvalidParam("Wrong email for session."))); -+ } -+ - match session.validation_state { - | ValidationState::Validated => { - // If the existing session is already valid, don't send an email. -@@ -119,7 +123,7 @@ pub async fn send_validation_email( - }, - | ValidationState::Pending(ref mut token) => { - // Check ratelimiting for the target address. -- if self.ratelimiter.check_key(&recipient.email).is_err() { -+ if self.ratelimiter.check_key(&session.email).is_err() { - return Err(Error::BadRequest( - ErrorKind::LimitExceeded { retry_after: None }, - "You're sending emails too fast, try again in a few minutes.", --- -2.55.0 - diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/0003-fix-backport-SEC26.patch b/pkgs/by-name/ma/matrix-continuwuity_latest/0003-fix-backport-SEC26.patch deleted file mode 100644 index 8736fdbeea72..000000000000 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/0003-fix-backport-SEC26.patch +++ /dev/null @@ -1,132 +0,0 @@ -From a00a615799bc55bc093a6298735732565aa2b566 Mon Sep 17 00:00:00 2001 -From: Ginger -Date: Tue, 11 Aug 2026 16:35:24 -0400 -Subject: [PATCH 2/2] fix(backport): SEC26 - -Reviewed-By: timedout -Reviewed-By: Ginger -(cherry picked from commit 700fbe472d4a8385b96f870256bfc00f9a15f809) ---- - src/api/server/event_auth.rs | 27 ++++++++++----------------- - src/api/server/state.rs | 13 ++++++++++++- - src/api/server/state_ids.rs | 13 ++++++++++++- - 3 files changed, 34 insertions(+), 19 deletions(-) - -diff --git a/src/api/server/event_auth.rs b/src/api/server/event_auth.rs -index a9019e8e7..433e18f46 100644 ---- a/src/api/server/event_auth.rs -+++ b/src/api/server/event_auth.rs -@@ -1,12 +1,9 @@ - use std::{borrow::Borrow, iter::once}; - - use axum::extract::State; --use conduwuit::{Err, Error, Result, info, utils::stream::ReadyExt}; -+use conduwuit::{Err, Result, Event, info, utils::stream::ReadyExt}; - use futures::StreamExt; --use ruma::{ -- RoomId, -- api::{client::error::ErrorKind, federation::authorization::get_event_authorization}, --}; -+use ruma::api::federation::authorization::get_event_authorization; - - use super::AccessCheck; - use crate::Ruma; -@@ -42,25 +39,21 @@ pub(crate) async fn get_event_authorization_route( - return Err!(Request(NotFound("This server is not participating in that room."))); - } - -- let event = services -+ // The event must be in the room we just authorised access to -+ if !services - .rooms - .timeline -- .get_pdu_json(&body.event_id) -+ .get_pdu(&body.event_id) - .await -- .map_err(|_| Error::BadRequest(ErrorKind::NotFound, "Event not found."))?; -- -- let room_id_str = event -- .get("room_id") -- .and_then(|val| val.as_str()) -- .ok_or_else(|| Error::bad_database("Invalid event in database."))?; -- -- let room_id = <&RoomId>::try_from(room_id_str) -- .map_err(|_| Error::bad_database("Invalid room_id in event in database."))?; -+ .is_ok_and(|pdu| pdu.room_id_or_hash() == body.room_id) -+ { -+ return Err!(Request(NotFound("Event not found."))); -+ } - - let auth_chain = services - .rooms - .auth_chain -- .event_ids_iter(room_id, once(body.event_id.borrow())) -+ .event_ids_iter(&body.room_id, once(body.event_id.borrow())) - .ready_filter_map(Result::ok) - .filter_map(|id| async move { services.rooms.timeline.get_pdu_json(&id).await.ok() }) - .then(|pdu| services.sending.convert_to_outgoing_federation_event(pdu)) -diff --git a/src/api/server/state.rs b/src/api/server/state.rs -index 5e1ad8ca2..05a008b74 100644 ---- a/src/api/server/state.rs -+++ b/src/api/server/state.rs -@@ -1,7 +1,7 @@ - use std::{borrow::Borrow, iter::once}; - - use axum::extract::State; --use conduwuit::{Err, Result, at, err, info, utils::IterStream}; -+use conduwuit::{Err, Event, Result, at, err, info, utils::IterStream}; - use futures::{FutureExt, StreamExt, TryStreamExt}; - use ruma::{OwnedEventId, api::federation::event::get_room_state}; - -@@ -24,6 +24,17 @@ pub(crate) async fn get_room_state_route( - .check() - .await?; - -+ // The event must be in the room we just authorised access to -+ if !services -+ .rooms -+ .timeline -+ .get_pdu(&body.event_id) -+ .await -+ .is_ok_and(|pdu| pdu.room_id_or_hash() == body.room_id) -+ { -+ return Err!(Request(NotFound("Event not found."))); -+ } -+ - if !services - .rooms - .state_cache -diff --git a/src/api/server/state_ids.rs b/src/api/server/state_ids.rs -index c9dea3116..206f3efc5 100644 ---- a/src/api/server/state_ids.rs -+++ b/src/api/server/state_ids.rs -@@ -1,7 +1,7 @@ - use std::{borrow::Borrow, iter::once}; - - use axum::extract::State; --use conduwuit::{Err, Result, at, err, info}; -+use conduwuit::{Err, Event, Result, at, err, info}; - use futures::{StreamExt, TryStreamExt}; - use ruma::{OwnedEventId, api::federation::event::get_room_state_ids}; - -@@ -25,6 +25,17 @@ pub(crate) async fn get_room_state_ids_route( - .check() - .await?; - -+ // The event must be in the room we just authorised access to -+ if !services -+ .rooms -+ .timeline -+ .get_pdu(&body.event_id) -+ .await -+ .is_ok_and(|pdu| pdu.room_id_or_hash() == body.room_id) -+ { -+ return Err!(Request(NotFound("Event not found."))); -+ } -+ - if !services - .rooms - .state_cache --- -2.55.0 - diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix index c94e635f7543..197b310f7f71 100644 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix @@ -50,12 +50,6 @@ rustPlatform.buildRustPackage (finalAttrs: { cargoHash = "sha256-uvMiFURXxkLbbbwq4pG5hevsLZHQ1wVfTNvzQRTQWxE="; - patches = [ - ./0001-fix-backport-SEC10.patch - ./0002-fix-backport-SEC28.patch - ./0003-fix-backport-SEC26.patch - ]; - nativeBuildInputs = [ pkg-config rustPlatform.bindgenHook From b349beec4542bceb3b9d0c7644d299cbe78563e0 Mon Sep 17 00:00:00 2001 From: Bart Oostveen Date: Fri, 10 Jul 2026 19:26:58 +0200 Subject: [PATCH 05/16] matrix-continuwuity_latest: 0.5.10 -> 26.6.1 Release notes: https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v26.6.1 and https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v26.6.0 Changelog: https://forgejo.ellis.link/continuwuation/continuwuity/src/commit/v26.6.1/CHANGELOG.md Diff: https://forgejo.ellis.link/continuwuation/continuwuity/compare/v0.5.10...v26.6.1 (cherry picked from commit ad3d97426255abeb146261dbf423c1ff3a21b6d0) --- .../ma/matrix-continuwuity_latest/package.nix | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix index 197b310f7f71..8070c6a0e50e 100644 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix @@ -23,13 +23,13 @@ let }).overrideAttrs ( final: old: { - version = "10.10.1"; + version = "11.1.1"; src = fetchFromGitea { domain = "forgejo.ellis.link"; owner = "continuwuation"; repo = "rocksdb"; - rev = "10.10.fb"; - hash = "sha256-1ef75IDMs5Hba4VWEyXPJb02JyShy5k4gJfzGDhopRk="; + rev = "3756b2b905e13216d8b56bcc783d814e7b073aff"; + hash = "sha256-rSv4fr2bf9JJwdodgeuPCuceeh7k97KVxrAOC0wyPQY="; }; patches = [ ]; @@ -38,17 +38,17 @@ let in rustPlatform.buildRustPackage (finalAttrs: { pname = "matrix-continuwuity"; - version = "0.5.10"; + version = "26.6.1"; src = fetchFromGitea { domain = "forgejo.ellis.link"; owner = "continuwuation"; repo = "continuwuity"; tag = "v${finalAttrs.version}"; - hash = "sha256-oevEGYlAK/rMJhm200CkwerT5oVak8sJj0Fa6r6+J/Q="; + hash = "sha256-sfUxhBPf0P46Xf0lcZFDj23cuZ9apJDyTQPa97jlcSY="; }; - cargoHash = "sha256-uvMiFURXxkLbbbwq4pG5hevsLZHQ1wVfTNvzQRTQWxE="; + cargoHash = "sha256-Qmw9Xum+Osu/4kbVqJP79gbtimpV2P7SkCMuQWyBYxg="; nativeBuildInputs = [ pkg-config From 23c88534655c46c4eb4be57a4fc9b44708b9d37a Mon Sep 17 00:00:00 2001 From: Bart Oostveen Date: Sun, 12 Jul 2026 23:41:21 +0200 Subject: [PATCH 06/16] matrix-continuwuity_latest: 26.6.1 -> 26.6.2 Release notes: https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v26.6.2 Changelog: https://forgejo.ellis.link/continuwuation/continuwuity/src/commit/v26.6.2/CHANGELOG.md Diff: https://forgejo.ellis.link/continuwuation/continuwuity/compare/v26.6.1...v26.6.2 (cherry picked from commit 9af645506c710799906e7a45bdcaa00fc9552b93) --- pkgs/by-name/ma/matrix-continuwuity_latest/package.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix index 8070c6a0e50e..01d249e77e99 100644 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix @@ -38,17 +38,17 @@ let in rustPlatform.buildRustPackage (finalAttrs: { pname = "matrix-continuwuity"; - version = "26.6.1"; + version = "26.6.2"; src = fetchFromGitea { domain = "forgejo.ellis.link"; owner = "continuwuation"; repo = "continuwuity"; tag = "v${finalAttrs.version}"; - hash = "sha256-sfUxhBPf0P46Xf0lcZFDj23cuZ9apJDyTQPa97jlcSY="; + hash = "sha256-GcCjJiUOGX+vF7R4IRgNQs8KpfVj+MXwnhofwSm6gpA="; }; - cargoHash = "sha256-Qmw9Xum+Osu/4kbVqJP79gbtimpV2P7SkCMuQWyBYxg="; + cargoHash = "sha256-p1Bz7op/qPogBn8bj9pQ7KjRhH2kZao8o0LPqWH2ZYo="; nativeBuildInputs = [ pkg-config From 34b47e9d5b5f626a79541ef3cfdf5ac7dfed5191 Mon Sep 17 00:00:00 2001 From: Bart Oostveen Date: Mon, 27 Jul 2026 22:30:38 +0200 Subject: [PATCH 07/16] matrix-continuwuity_latest: 26.6.2 -> 26.7.1 Release notes: https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v26.7.1 Changelog: https://forgejo.ellis.link/continuwuation/continuwuity/src/commit/v26.7.1/CHANGELOG.md Diff: https://forgejo.ellis.link/continuwuation/continuwuity/compare/v26.6.2...v26.7.1 (cherry picked from commit 59cec7483b2f79e066aa5ff6ed50ee080eed9de9) --- pkgs/by-name/ma/matrix-continuwuity_latest/package.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix index 01d249e77e99..23e2cfcbd6b8 100644 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix @@ -38,17 +38,17 @@ let in rustPlatform.buildRustPackage (finalAttrs: { pname = "matrix-continuwuity"; - version = "26.6.2"; + version = "26.7.1"; src = fetchFromGitea { domain = "forgejo.ellis.link"; owner = "continuwuation"; repo = "continuwuity"; tag = "v${finalAttrs.version}"; - hash = "sha256-GcCjJiUOGX+vF7R4IRgNQs8KpfVj+MXwnhofwSm6gpA="; + hash = "sha256-gdTj7y1fwTG0CNWeK/0An6WM8YEeiw7TOuTjh1Zbe50="; }; - cargoHash = "sha256-p1Bz7op/qPogBn8bj9pQ7KjRhH2kZao8o0LPqWH2ZYo="; + cargoHash = "sha256-nhL4GRmw49n+UlHUKHcEJPoT/BNm+6eatqz/m+zjahg="; nativeBuildInputs = [ pkg-config From d038c83a1f8d87cddad3d7b380f6a4a808adfc92 Mon Sep 17 00:00:00 2001 From: Bart Oostveen Date: Wed, 15 Jul 2026 21:49:44 +0200 Subject: [PATCH 08/16] matrix-continuwuity_latest: make rocksdb overridable (cherry picked from commit 8665aa3f1cd122ea31cd870ec39e8926d0fc4f03) --- .../ma/matrix-continuwuity_latest/package.nix | 30 ++++--------------- .../ma/matrix-continuwuity_latest/rocksdb.nix | 26 ++++++++++++++++ 2 files changed, 31 insertions(+), 25 deletions(-) create mode 100644 pkgs/by-name/ma/matrix-continuwuity_latest/rocksdb.nix diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix index 23e2cfcbd6b8..1220c8596fbd 100644 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix @@ -6,7 +6,7 @@ bzip2, zstd, stdenv, - rocksdb, + callPackage, nix-update-script, testers, matrix-continuwuity_latest, @@ -14,28 +14,7 @@ liburing, nixosTests, }: -let - rocksdb' = - (rocksdb.override { - # rocksdb does not support prefixed jemalloc, which is required on darwin - enableJemalloc = !stdenv.hostPlatform.isDarwin; - jemalloc = rust-jemalloc-sys-unprefixed; - }).overrideAttrs - ( - final: old: { - version = "11.1.1"; - src = fetchFromGitea { - domain = "forgejo.ellis.link"; - owner = "continuwuation"; - repo = "rocksdb"; - rev = "3756b2b905e13216d8b56bcc783d814e7b073aff"; - hash = "sha256-rSv4fr2bf9JJwdodgeuPCuceeh7k97KVxrAOC0wyPQY="; - }; - patches = [ ]; - } - ); -in rustPlatform.buildRustPackage (finalAttrs: { pname = "matrix-continuwuity"; version = "26.7.1"; @@ -64,12 +43,13 @@ rustPlatform.buildRustPackage (finalAttrs: { env = { ZSTD_SYS_USE_PKG_CONFIG = true; - ROCKSDB_INCLUDE_DIR = "${rocksdb'}/include"; - ROCKSDB_LIB_DIR = "${rocksdb'}/lib"; + ROCKSDB_INCLUDE_DIR = "${finalAttrs.rocksdb}/include"; + ROCKSDB_LIB_DIR = "${finalAttrs.rocksdb}/lib"; }; + rocksdb = callPackage ./rocksdb.nix { }; # make used rocksdb version available (e.g., for backup scripts) + passthru = { - rocksdb = rocksdb'; # make used rocksdb version available (e.g., for backup scripts) updateScript = nix-update-script { }; tests = { version = testers.testVersion { diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/rocksdb.nix b/pkgs/by-name/ma/matrix-continuwuity_latest/rocksdb.nix new file mode 100644 index 000000000000..fcdeb4b77076 --- /dev/null +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/rocksdb.nix @@ -0,0 +1,26 @@ +{ + stdenv, + fetchFromGitea, + rocksdb, + rust-jemalloc-sys-unprefixed, +}: + +(rocksdb.override { + # rocksdb does not support prefixed jemalloc, which is required on darwin + enableJemalloc = !stdenv.hostPlatform.isDarwin; + jemalloc = rust-jemalloc-sys-unprefixed; +}).overrideAttrs + ( + final: old: { + version = "11.1.1"; + src = fetchFromGitea { + domain = "forgejo.ellis.link"; + owner = "continuwuation"; + repo = "rocksdb"; + rev = "3756b2b905e13216d8b56bcc783d814e7b073aff"; + hash = "sha256-rSv4fr2bf9JJwdodgeuPCuceeh7k97KVxrAOC0wyPQY="; + }; + + patches = [ ]; + } + ) From 52ef021d5b440777a1ba948632ed0e4815240851 Mon Sep 17 00:00:00 2001 From: Bart Oostveen Date: Thu, 30 Jul 2026 20:27:54 +0200 Subject: [PATCH 09/16] matrix-continuwuity_latest: 26.7.1 -> 26.7.2 Release notes: https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v26.7.2 Changelog: https://forgejo.ellis.link/continuwuation/continuwuity/src/commit/3f69334f9af2b09ae2ad786e16ccf7a5d4a5924f/CHANGELOG.md Diff: https://forgejo.ellis.link/continuwuation/continuwuity/compare/v26.7.1...v26.7.2 (cherry picked from commit be742101ac9ac42a2de6f974b2dc4fa532635acb) --- pkgs/by-name/ma/matrix-continuwuity_latest/package.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix index 1220c8596fbd..88f63a254e87 100644 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix @@ -17,17 +17,17 @@ rustPlatform.buildRustPackage (finalAttrs: { pname = "matrix-continuwuity"; - version = "26.7.1"; + version = "26.7.2"; src = fetchFromGitea { domain = "forgejo.ellis.link"; owner = "continuwuation"; repo = "continuwuity"; tag = "v${finalAttrs.version}"; - hash = "sha256-gdTj7y1fwTG0CNWeK/0An6WM8YEeiw7TOuTjh1Zbe50="; + hash = "sha256-uE38tYYgze2q4hgW1mzk5CLTD3ezAwCnj+RQOmZtCdw="; }; - cargoHash = "sha256-nhL4GRmw49n+UlHUKHcEJPoT/BNm+6eatqz/m+zjahg="; + cargoHash = "sha256-DZsRr0Xt/7HnlNCZfXK4dUILK/uEOnSD+r26OxvycD0="; nativeBuildInputs = [ pkg-config From a25a7ff15604063fb18ed217409e6dff0298399b Mon Sep 17 00:00:00 2001 From: Bart Oostveen Date: Tue, 11 Aug 2026 22:20:39 +0200 Subject: [PATCH 10/16] nixos/tests/matrix-continuwuity: wrap sync filter in filter data class This ensures the filter is sent to the sync endpoint properly. Originally found this shortcoming of the refactored test in my personal package repository, this commit upstreams the fix. Continuwuity just ignores incorrect sync filters, so this just exists for completeness. (cherry picked from commit 0949151f36f81cb13be43ac44ce27c043e084194) --- nixos/tests/matrix/continuwuity.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nixos/tests/matrix/continuwuity.nix b/nixos/tests/matrix/continuwuity.nix index 80c46fb21362..f84d0bd39c20 100644 --- a/nixos/tests/matrix/continuwuity.nix +++ b/nixos/tests/matrix/continuwuity.nix @@ -29,7 +29,7 @@ in import asyncio from mautrix.client import Client - from mautrix.types import EventType, RoomFilter + from mautrix.types import EventType, RoomFilter, Filter async def main() -> None: @@ -66,7 +66,7 @@ in received.set() client.add_event_handler(EventType.ROOM_MESSAGE, on_message) - sync_task = client.start(RoomFilter(rooms=[room_id])) + sync_task = client.start(Filter(room=RoomFilter(rooms=[room_id]))) await client.send_text(room_id, msg) From b03ac3a988b858d082a7db27529d298a650ddb78 Mon Sep 17 00:00:00 2001 From: Bart Oostveen Date: Tue, 11 Aug 2026 23:56:09 +0200 Subject: [PATCH 11/16] matrix-continuwuity_latest: 26.7.2 -> 26.7.3 Release notes: https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v26.7.3 Changelog: https://forgejo.ellis.link/continuwuation/continuwuity/src/commit/v26.7.3/CHANGELOG.md Diff: https://forgejo.ellis.link/continuwuation/continuwuity/compare/v26.7.2...v26.7.3 Fixes: GHSA-v2x6-m99h-vqxx (cherry picked from commit d578214c2746c7830918777a16d45074795ac37a) --- pkgs/by-name/ma/matrix-continuwuity_latest/package.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix index 88f63a254e87..1f16b3fdc466 100644 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix @@ -17,17 +17,17 @@ rustPlatform.buildRustPackage (finalAttrs: { pname = "matrix-continuwuity"; - version = "26.7.2"; + version = "26.7.3"; src = fetchFromGitea { domain = "forgejo.ellis.link"; owner = "continuwuation"; repo = "continuwuity"; tag = "v${finalAttrs.version}"; - hash = "sha256-uE38tYYgze2q4hgW1mzk5CLTD3ezAwCnj+RQOmZtCdw="; + hash = "sha256-Uj4fcHoTiQ/G5vyEnSOatjdqaUquGSlOxeTt32F2xew="; }; - cargoHash = "sha256-DZsRr0Xt/7HnlNCZfXK4dUILK/uEOnSD+r26OxvycD0="; + cargoHash = "sha256-6C4dXm9+m2sKA4m1YXocifX4ss61s6/L2FeXzfkowMs="; nativeBuildInputs = [ pkg-config From ff91559a594f3b73a1fad08e85b6d4b39f195f66 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 22 Aug 2026 20:39:39 -0400 Subject: [PATCH 12/16] matrix-continuwuity_latest: 26.7.3 -> 26.8.1 (cherry picked from commit eef928cd8012922a92234729882466fce5b0ca7c) --- pkgs/by-name/ma/matrix-continuwuity_latest/package.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix index 1f16b3fdc466..4b0eab5f5c0c 100644 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix @@ -17,17 +17,17 @@ rustPlatform.buildRustPackage (finalAttrs: { pname = "matrix-continuwuity"; - version = "26.7.3"; + version = "26.8.1"; src = fetchFromGitea { domain = "forgejo.ellis.link"; owner = "continuwuation"; repo = "continuwuity"; tag = "v${finalAttrs.version}"; - hash = "sha256-Uj4fcHoTiQ/G5vyEnSOatjdqaUquGSlOxeTt32F2xew="; + hash = "sha256-slwfK5aEIFjIue01L4nVUHaLx/2j3nRKrmkNkfPPZXE="; }; - cargoHash = "sha256-6C4dXm9+m2sKA4m1YXocifX4ss61s6/L2FeXzfkowMs="; + cargoHash = "sha256-s87vdh+5rMNIXDWt2dZZvqSY9dJzEHuvEhJLG1HvphY="; nativeBuildInputs = [ pkg-config From d1c66d82cad062b31410e8c4d05b3792c7d53e58 Mon Sep 17 00:00:00 2001 From: Bart Oostveen Date: Wed, 16 Sep 2026 22:29:30 +0200 Subject: [PATCH 13/16] matrix-continuwuity_latest: 26.8.1 -> 26.9.0 Release notes: https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v26.9.0 Changelog: https://forgejo.ellis.link/continuwuation/continuwuity/src/commit/v26.9.0/CHANGELOG.md Diff: https://forgejo.ellis.link/continuwuation/continuwuity/compare/v26.8.1...v26.9.0 (cherry picked from commit 5036aeb14e98d51d4f2a2208971c74282cce7e0f) --- pkgs/by-name/ma/matrix-continuwuity_latest/package.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix index 4b0eab5f5c0c..9a2635808322 100644 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix @@ -17,17 +17,17 @@ rustPlatform.buildRustPackage (finalAttrs: { pname = "matrix-continuwuity"; - version = "26.8.1"; + version = "26.9.0"; src = fetchFromGitea { domain = "forgejo.ellis.link"; owner = "continuwuation"; repo = "continuwuity"; tag = "v${finalAttrs.version}"; - hash = "sha256-slwfK5aEIFjIue01L4nVUHaLx/2j3nRKrmkNkfPPZXE="; + hash = "sha256-w/sFqEbvzNTbvR8eTeVT0qo/t0p/vlQUna1bPOqJs7E="; }; - cargoHash = "sha256-s87vdh+5rMNIXDWt2dZZvqSY9dJzEHuvEhJLG1HvphY="; + cargoHash = "sha256-ubIQNgn/FM++WkmnfDOVUpxfBCElIYJxvNMipwXbmIc="; nativeBuildInputs = [ pkg-config From c82fa6b46a3b3fdb8f32927c5d997e240bdc96a8 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 27 Sep 2026 21:48:24 -0400 Subject: [PATCH 14/16] matrix-continuwuity_latest: 26.9.0 -> 26.9.1 (cherry picked from commit 32f8806c23d3f414216f3ac21eb9b971a331b7e4) --- pkgs/by-name/ma/matrix-continuwuity_latest/package.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix index 9a2635808322..0b3e4e2e438d 100644 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix @@ -17,17 +17,17 @@ rustPlatform.buildRustPackage (finalAttrs: { pname = "matrix-continuwuity"; - version = "26.9.0"; + version = "26.9.1"; src = fetchFromGitea { domain = "forgejo.ellis.link"; owner = "continuwuation"; repo = "continuwuity"; tag = "v${finalAttrs.version}"; - hash = "sha256-w/sFqEbvzNTbvR8eTeVT0qo/t0p/vlQUna1bPOqJs7E="; + hash = "sha256-z9iOdSjTvv+kDJaoAyKTzrY0ge2KM90H4tM8ir52RMQ="; }; - cargoHash = "sha256-ubIQNgn/FM++WkmnfDOVUpxfBCElIYJxvNMipwXbmIc="; + cargoHash = "sha256-dbTrgE0+OzHhD3rjQciZfRIBivvZa0YZyKpTP8XOzZI="; nativeBuildInputs = [ pkg-config From 1ad7b02e9be0dcf1827ca4903862be4b41af0362 Mon Sep 17 00:00:00 2001 From: Bart Oostveen Date: Mon, 28 Sep 2026 09:55:53 +0200 Subject: [PATCH 15/16] nixos/tests/matrix-continuwuity: use matrix-continuwuity_latest Not-cherry-picked-because: otherwise this test would fail because matrix-continuwuity is marked as insecure. --- nixos/tests/matrix/continuwuity.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nixos/tests/matrix/continuwuity.nix b/nixos/tests/matrix/continuwuity.nix index f84d0bd39c20..2a6b5e35e749 100644 --- a/nixos/tests/matrix/continuwuity.nix +++ b/nixos/tests/matrix/continuwuity.nix @@ -1,4 +1,4 @@ -{ lib, ... }: +{ lib, pkgs, ... }: let name = "continuwuity"; user = "alice"; @@ -11,6 +11,7 @@ in continuwuity = { services.matrix-continuwuity = { enable = true; + package = pkgs.matrix-continuwuity_latest; settings.global = { server_name = name; address = [ "0.0.0.0" ]; From 73c68ad3f5acb7ce80eac13e970de188eb268d42 Mon Sep 17 00:00:00 2001 From: Bart Oostveen Date: Mon, 28 Sep 2026 10:11:33 +0200 Subject: [PATCH 16/16] matrix-continuwuity_latest: enable structuredAttrs Not-cherry-picked-because: package not in unstable --- pkgs/by-name/ma/matrix-continuwuity_latest/package.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix index 0b3e4e2e438d..7e599477499d 100644 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix @@ -19,6 +19,8 @@ rustPlatform.buildRustPackage (finalAttrs: { pname = "matrix-continuwuity"; version = "26.9.1"; + __structuredAttrs = true; + src = fetchFromGitea { domain = "forgejo.ellis.link"; owner = "continuwuation";