From 5bf20db8e79f07aed49e0cc510d7522dcbaf9e9a Mon Sep 17 00:00:00 2001 From: Nyx Date: Sat, 12 Jul 2025 03:50:26 -0500 Subject: [PATCH 01/28] Add /_continuwuity/ paths --- docs/deploying/generic.md | 2 +- src/api/router.rs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/deploying/generic.md b/docs/deploying/generic.md index 9128f346..cfa9b32f 100644 --- a/docs/deploying/generic.md +++ b/docs/deploying/generic.md @@ -172,7 +172,7 @@ As we would prefer our users to use Caddy, we will not provide configuration fil You will need to reverse proxy everything under following routes: - `/_matrix/` - core Matrix C-S and S-S APIs -- `/_conduwuit/` - ad-hoc Continuwuity routes such as `/local_user_count` and +- `/_conduwuit/` and/or `/_continuwuity/` - ad-hoc Continuwuity routes such as `/local_user_count` and `/server_version` You can optionally reverse proxy the following individual routes: diff --git a/src/api/router.rs b/src/api/router.rs index d1b05a91..8072fa5b 100644 --- a/src/api/router.rs +++ b/src/api/router.rs @@ -187,6 +187,7 @@ pub fn build(router: Router, server: &Server) -> Router { .ruma_route(&client::well_known_support) .ruma_route(&client::well_known_client) .route("/_conduwuit/server_version", get(client::conduwuit_server_version)) + .route("/_continuwuity/server_version", get(client::conduwuit_server_version)) .ruma_route(&client::room_initial_sync_route) .route("/client/server.json", get(client::syncv3_client_server_json)); @@ -226,13 +227,15 @@ pub fn build(router: Router, server: &Server) -> Router { .ruma_route(&server::well_known_server) .ruma_route(&server::get_content_route) .ruma_route(&server::get_content_thumbnail_route) - .route("/_conduwuit/local_user_count", get(client::conduwuit_local_user_count)); + .route("/_conduwuit/local_user_count", get(client::conduwuit_local_user_count)) + .route("/_continuwuity/local_user_count", get(client::conduwuit_local_user_count)); } else { router = router .route("/_matrix/federation/*path", any(federation_disabled)) .route("/.well-known/matrix/server", any(federation_disabled)) .route("/_matrix/key/*path", any(federation_disabled)) - .route("/_conduwuit/local_user_count", any(federation_disabled)); + .route("/_conduwuit/local_user_count", any(federation_disabled)) + .route("/_continuwuity/local_user_count", any(federation_disabled)); } if config.allow_legacy_media { From 6aceac38332ea36d54c44b6c3203987f58387642 Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Tue, 1 Jul 2025 23:30:13 +0100 Subject: [PATCH 02/28] docs: Note policy on large formatting diffs --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1c091183..cf140ea1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,6 +21,8 @@ comment saying why. Do not write inefficient code for the sake of satisfying lints. If a lint is wrong and provides a more inefficient solution or suggestion, allow the lint and mention that in a comment. +If there is a large formatting change across unrelated files, make a separate commit so that it can be added to the `.git-blame-ignore-revs` file. + ### Pre-commit Checks Continuwuity uses pre-commit hooks to enforce various coding standards and catch common issues before they're committed. These checks include: From cfc64ddb40bbe0d1a9dba7000d3795dfd9b07046 Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Tue, 1 Jul 2025 23:32:09 +0100 Subject: [PATCH 03/28] docs: Note python requirements --- CONTRIBUTING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cf140ea1..106a349e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,6 +38,10 @@ You can run these checks locally by installing [prefligit](https://github.com/j1 ```bash +# Requires UV: +# Mac/linux: curl -LsSf https://astral.sh/uv/install.sh | sh +# Windows: powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" + # Install prefligit using cargo-binstall cargo binstall prefligit @@ -50,6 +54,8 @@ prefligit --all-files Alternatively, you can use [pre-commit](https://pre-commit.com/): ```bash +# Requires python + # Install pre-commit pip install pre-commit From 6a4905271ecf4252351a80df797d658a327bf5cd Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Tue, 1 Jul 2025 23:57:24 +0100 Subject: [PATCH 04/28] refactor: Add with_lock traits --- src/core/utils/mod.rs | 1 + src/core/utils/with_lock.rs | 65 +++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/core/utils/with_lock.rs diff --git a/src/core/utils/mod.rs b/src/core/utils/mod.rs index 54404e4c..329e2ea2 100644 --- a/src/core/utils/mod.rs +++ b/src/core/utils/mod.rs @@ -19,6 +19,7 @@ pub mod sys; #[cfg(test)] mod tests; pub mod time; +pub mod with_lock; pub use ::conduwuit_macros::implement; pub use ::ctor::{ctor, dtor}; diff --git a/src/core/utils/with_lock.rs b/src/core/utils/with_lock.rs new file mode 100644 index 00000000..76f014d1 --- /dev/null +++ b/src/core/utils/with_lock.rs @@ -0,0 +1,65 @@ +//! Traits for explicitly scoping the lifetime of locks. + +use std::sync::{Arc, Mutex}; + +pub trait WithLock { + /// Acquires a lock and executes the given closure with the locked data. + fn with_lock(&self, f: F) + where + F: FnMut(&mut T); +} + +impl WithLock for Mutex { + fn with_lock(&self, mut f: F) + where + F: FnMut(&mut T), + { + // The locking and unlocking logic is hidden inside this function. + let mut data_guard = self.lock().unwrap(); + f(&mut data_guard); + // Lock is released here when `data_guard` goes out of scope. + } +} + +impl WithLock for Arc> { + fn with_lock(&self, mut f: F) + where + F: FnMut(&mut T), + { + // The locking and unlocking logic is hidden inside this function. + let mut data_guard = self.lock().unwrap(); + f(&mut data_guard); + // Lock is released here when `data_guard` goes out of scope. + } +} + +pub trait WithLockAsync { + /// Acquires a lock and executes the given closure with the locked data. + fn with_lock(&self, f: F) -> impl Future + where + F: FnMut(&mut T); +} + +impl WithLockAsync for futures::lock::Mutex { + async fn with_lock(&self, mut f: F) + where + F: FnMut(&mut T), + { + // The locking and unlocking logic is hidden inside this function. + let mut data_guard = self.lock().await; + f(&mut data_guard); + // Lock is released here when `data_guard` goes out of scope. + } +} + +impl WithLockAsync for Arc> { + async fn with_lock(&self, mut f: F) + where + F: FnMut(&mut T), + { + // The locking and unlocking logic is hidden inside this function. + let mut data_guard = self.lock().await; + f(&mut data_guard); + // Lock is released here when `data_guard` goes out of scope. + } +} From b17f27880354c16544dac5cfde0f44b5a6341c0b Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Wed, 2 Jul 2025 00:44:28 +0100 Subject: [PATCH 05/28] docs: Add code style guide --- CONTRIBUTING.md | 55 +----- docs/SUMMARY.md | 1 + docs/development.md | 2 +- docs/development/code_style.md | 331 +++++++++++++++++++++++++++++++++ 4 files changed, 342 insertions(+), 47 deletions(-) create mode 100644 docs/development/code_style.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 106a349e..d6a3342b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,27 +1,15 @@ # Contributing guide This page is about contributing to Continuwuity. The -[development](./development.md) page may be of interest for you as well. +[development](./development.md) and [code style guide](./development/code_style.md) pages may be of interest for you as well. If you would like to work on an [issue][issues] that is not assigned, preferably ask in the Matrix room first at [#continuwuity:continuwuity.org][continuwuity-matrix], and comment on it. -### Linting and Formatting +### Code Style -It is mandatory all your changes satisfy the lints (clippy, rustc, rustdoc, etc) -and your code is formatted via the **nightly** rustfmt (`cargo +nightly fmt`). A lot of the -`rustfmt.toml` features depend on nightly toolchain. It would be ideal if they -weren't nightly-exclusive features, but they currently still are. CI's rustfmt -uses nightly. - -If you need to allow a lint, please make sure it's either obvious as to why -(e.g. clippy saying redundant clone but it's actually required) or it has a -comment saying why. Do not write inefficient code for the sake of satisfying -lints. If a lint is wrong and provides a more inefficient solution or -suggestion, allow the lint and mention that in a comment. - -If there is a large formatting change across unrelated files, make a separate commit so that it can be added to the `.git-blame-ignore-revs` file. +Please review and follow the [code style guide](./development/code_style.md) for formatting, linting, naming conventions, and other code standards. ### Pre-commit Checks @@ -66,7 +54,7 @@ pre-commit install pre-commit run --all-files ``` -These same checks are run in CI via the prefligit-checks workflow to ensure consistency. +These same checks are run in CI via the prefligit-checks workflow to ensure consistency. These must pass before the PR is merged. ### Running tests locally @@ -115,37 +103,13 @@ To build the documentation locally: The output of the mdbook generation is in `public/`. You can open the HTML files directly in your browser without needing a web server. -### Inclusivity and Diversity - -All **MUST** code and write with inclusivity and diversity in mind. See the -[following page by Google on writing inclusive code and -documentation](https://developers.google.com/style/inclusive-documentation). - -This **EXPLICITLY** forbids usage of terms like "blacklist"/"whitelist" and -"master"/"slave", [forbids gender-specific words and -phrases](https://developers.google.com/style/pronouns#gender-neutral-pronouns), -forbids ableist language like "sanity-check", "cripple", or "insane", and -forbids culture-specific language (e.g. US-only holidays or cultures). - -No exceptions are allowed. Dependencies that may use these terms are allowed but -[do not replicate the name in your functions or -variables](https://developers.google.com/style/inclusive-documentation#write-around). - -In addition to language, write and code with the user experience in mind. This -is software that intends to be used by everyone, so make it easy and comfortable -for everyone to use. 🏳️‍⚧️ - -### Variable, comment, function, etc standards - -Rust's default style and standards with regards to [function names, variable -names, comments](https://rust-lang.github.io/api-guidelines/naming.html), etc -applies here. ### Commit Messages Continuwuity follows the [Conventional Commits](https://www.conventionalcommits.org/) specification for commit messages. This provides a standardized format that makes the commit history more readable and enables automated tools to generate changelogs. The basic structure is: + ``` [(optional scope)]: @@ -186,11 +150,10 @@ of it, especially when the CI completed successfully and everything so it Before submitting a pull request, please ensure: 1. Your code passes all CI checks (formatting, linting, typo detection, etc.) -2. Your commit messages follow the conventional commits format -3. Tests are added for new functionality -4. Documentation is updated if needed - - +2. Your code follows the [code style guide](./development/code_style.md) +3. Your commit messages follow the conventional commits format +4. Tests are added for new functionality +5. Documentation is updated if needed Direct all PRs/MRs to the `main` branch. diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index b38009a1..fa097238 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -18,6 +18,7 @@ - [Admin Command Reference](admin_reference.md) - [Development](development.md) - [Contributing](contributing.md) + - [Code Style Guide](development/code_style.md) - [Testing](development/testing.md) - [Hot Reloading ("Live" Development)](development/hot_reload.md) - [Community (and Guidelines)](community.md) diff --git a/docs/development.md b/docs/development.md index 68b963c8..dd587da4 100644 --- a/docs/development.md +++ b/docs/development.md @@ -2,7 +2,7 @@ Information about developing the project. If you are only interested in using it, you can safely ignore this page. If you plan on contributing, see the -[contributor's guide](./contributing.md). +[contributor's guide](./contributing.md) and [code style guide](./development/code_style.md). ## Continuwuity project layout diff --git a/docs/development/code_style.md b/docs/development/code_style.md new file mode 100644 index 00000000..6051c787 --- /dev/null +++ b/docs/development/code_style.md @@ -0,0 +1,331 @@ +# Code Style Guide + +This guide outlines the coding standards and best practices for Continuwuity development. These guidelines help avoid bugs and maintain code consistency, readability, and quality across the project. + +These guidelines apply to new code on a best-effort basis. When modifying existing code, follow existing patterns in the immediate area you're changing and then gradually improve code style when making substantial changes. + +## General Principles + +- **Clarity over cleverness**: Write code that is easy to understand and maintain +- **Consistency**: Pragmatically follow existing patterns in the codebase, rather than adding new dependencies. +- **Safety**: Prefer safe, explicit code over unsafe code with implicit requirements +- **Performance**: Consider performance implications, but not at the expense of correctness or maintainability + +## Formatting and Linting + +All code must satisfy lints (clippy, rustc, rustdoc, etc) and be formatted using **nightly** rustfmt (`cargo +nightly fmt`). Many of the `rustfmt.toml` features depend on the nightly toolchain. + +If you need to allow a lint, ensure it's either obvious why (e.g. clippy saying redundant clone but it's actually required) or add a comment explaining the reason. Do not write inefficient code just to satisfy lints. If a lint is wrong and provides a less efficient solution, allow the lint and mention that in a comment. + +If making large formatting changes across unrelated files, create a separate commit so it can be added to the `.git-blame-ignore-revs` file. + +## Rust-Specific Guidelines + +### Naming Conventions + +Follow standard Rust naming conventions as outlined in the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/naming.html): + +- Use `snake_case` for functions, variables, and modules +- Use `PascalCase` for types, traits, and enum variants +- Use `SCREAMING_SNAKE_CASE` for constants and statics +- Use descriptive names that clearly indicate purpose + +```rs +// Good +fn process_user_request(user_id: &UserId) -> Result { ... } + +const MAX_RETRY_ATTEMPTS: usize = 3; + +struct UserSession { + session_id: String, + created_at: SystemTime, +} + +// Avoid +fn proc_reqw(id: &str) -> Result { ... } +``` + +### Error Handling + +- Use `Result` for operations that can fail +- Prefer specific error types over generic ones +- Use `?` operator for error propagation +- Provide meaningful error messages +- If needed, create or use an error enum. + +```rs +// Good +fn parse_server_name(input: &str) -> Result { + ServerName::parse(input) + .map_err(|_| InvalidServerNameError::new(input)) +} + +// Avoid +fn parse_server_name(input: &str) -> Result> { + Ok(ServerName::parse(input).unwrap()) +} +``` + +### Option Handling + +- Prefer explicit `Option` handling over unwrapping +- Use combinators like `map`, `and_then`, `unwrap_or_else` when appropriate + +```rs +// Good +let display_name = user.display_name + .as_ref() + .map(|name| name.trim()) + .filter(|name| !name.is_empty()) + .unwrap_or(&user.localpart); + +// Avoid +let display_name = if user.display_name.is_some() { + user.display_name.as_ref().unwrap() +} else { + &user.localpart +}; +``` + +## Logging Guidelines + +### Structured Logging + +**Always use structured logging instead of string interpolation.** This improves log parsing, filtering, and observability. + +```rs +// Good - structured parameters +debug!( + room_id = %room_id, + user_id = %user_id, + event_type = ?event.event_type(), + "Processing room event" +); + +info!( + server_name = %server_name, + response_time_ms = response_time.as_millis(), + "Federation request completed successfully" +); + +// Avoid - string interpolation +debug!("Processing room event for {room_id} from {user_id}"); +info!("Federation request to {server_name} took {response_time:?}"); +``` + +### Log Levels + +Use appropriate log levels: + +- `error!`: Unrecoverable errors that affect functionality +- `warn!`: Potentially problematic situations that don't stop execution +- `info!`: General information about application flow +- `debug!`: Detailed information for debugging +- `trace!`: Very detailed information, typically only useful during development + +Keep in mind the frequency that the log will be reached, and the relevancy to a server operator. + +```rs +// Good +error!( + error = %err, + room_id = %room_id, + "Failed to send event to room" +); + +warn!( + server_name = %server_name, + attempt = retry_count, + "Federation request failed, retrying" +); + +info!( + user_id = %user_id, + "User registered successfully" +); + +debug!( + event_id = %event_id, + auth_events = ?auth_event_ids, + "Validating event authorization" +); +``` + +### Sensitive Information + +Never log sensitive information such as: +- Access tokens +- Passwords +- Private keys +- Personal user data (unless specifically needed for debugging) + +```rs +// Good +debug!( + user_id = %user_id, + session_id = %session_id, + "Processing authenticated request" +); + +// Avoid +debug!( + user_id = %user_id, + access_token = %access_token, + "Processing authenticated request" +); +``` + +## Lock Management + +### Explicit Lock Scopes + +**Always use closure guards instead of implicitly dropped guards.** This makes lock scopes explicit and helps prevent deadlocks. + +Use the `WithLock` trait from `continuwuity-core::utils::with_lock`: + +```rs +use continuwuity_core::utils::with_lock::WithLock; + +// Good - explicit closure guard +shared_data.with_lock(|data| { + data.counter += 1; + data.last_updated = SystemTime::now(); + // Lock is explicitly released here +}); + +// Avoid - implicit guard +{ + let mut data = shared_data.lock().unwrap(); + data.counter += 1; + data.last_updated = SystemTime::now(); + // Lock released when guard goes out of scope - less explicit +} +``` + +For async contexts, use the async variant: + +```rs +use continuwuity_core::utils::with_lock::WithLockAsync; + +// Good - async closure guard +async_shared_data.with_lock(|data| { + data.process_async_update(); +}).await; +``` + +### Lock Ordering + +When acquiring multiple locks, always acquire them in a consistent order to prevent deadlocks: + +```rs +// Good - consistent ordering (e.g., by memory address or logical hierarchy) +let locks = [&lock_a, &lock_b, &lock_c]; +locks.sort_by_key(|lock| lock as *const _ as usize); + +for lock in locks { + lock.with_lock(|data| { + // Process data + }); +} + +// Avoid - inconsistent ordering that can cause deadlocks +lock_b.with_lock(|data_b| { + lock_a.with_lock(|data_a| { + // Deadlock risk if another thread acquires in A->B order + }); +}); +``` + +## Documentation + +### Code Comments + +- Reference related documentation or parts of the specification +- When a task has multiple ways of being acheved, explain your reasoning for your decision +- Update comments when code changes + +```rs +/// Processes a federation request with automatic retries and backoff. +/// +/// Implements exponential backoff to handle temporary +/// network issues and server overload gracefully. +pub async fn send_federation_request( + destination: &ServerName, + request: FederationRequest, +) -> Result { + // Retry with exponential backoff because federation can be flaky + // due to network issues or temporary server overload + let mut retry_delay = Duration::from_millis(100); + + for attempt in 1..=MAX_RETRIES { + match try_send_request(destination, &request).await { + Ok(response) => return Ok(response), + Err(err) if err.is_retriable() && attempt < MAX_RETRIES => { + warn!( + destination = %destination, + attempt = attempt, + error = %err, + retry_delay_ms = retry_delay.as_millis(), + "Federation request failed, retrying" + ); + + tokio::time::sleep(retry_delay).await; + retry_delay *= 2; // Exponential backoff + } + Err(err) => return Err(err), + } + } + + unreachable!("Loop should have returned or failed by now") +} +``` + +### Async Patterns + +- Use `async`/`await` appropriately +- Avoid blocking operations in async contexts +- Consider using `tokio::task::spawn_blocking` for CPU-intensive work + +```rs +// Good - non-blocking async operation +pub async fn fetch_user_profile( + &self, + user_id: &UserId, +) -> Result { + let profile = self.db + .get_user_profile(user_id) + .await?; + + Ok(profile) +} + +// Good - CPU-intensive work moved to blocking thread +pub async fn generate_thumbnail( + &self, + image_data: Vec, +) -> Result, Error> { + tokio::task::spawn_blocking(move || { + image::generate_thumbnail(image_data) + }) + .await + .map_err(|_| Error::TaskJoinError)? +} +``` + +## Inclusivity and Diversity Guidelines + +All code and documentation must be written with inclusivity and diversity in mind. This ensures our software is welcoming and accessible to all users and contributors. Follow the [Google guide on writing inclusive code and documentation](https://developers.google.com/style/inclusive-documentation) for comprehensive guidance. + +The following types of language are explicitly forbidden in all code, comments, documentation, and commit messages: + +**Ableist language:** Avoid terms like "sanity check", "crazy", "insane", "cripple", or "blind to". Use alternatives like "validation", "unexpected", "disable", or "unaware of". + +**Socially-charged technical terms:** Replace overly divisive terminology with neutral alternatives: +- "whitelist/blacklist" → "allowlist/denylist" or "permitted/blocked" +- "master/slave" → "primary/replica", "controller/worker", or "parent/child" + +When working with external dependencies that use non-inclusive terminology, avoid propagating them in your own APIs and variable names. + +Use diverse examples in documentation that avoid culturally-specific references, assumptions about user demographics, or unnecessarily gendered language. Design with accessibility and inclusivity in mind by providing clear error messages and considering diverse user needs. + +This software is intended to be used by everyone regardless of background, identity, or ability. Write code and documentation that reflects this commitment to inclusivity. From 946449d3e552beec8627ebb18512f4489d25681d Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Wed, 2 Jul 2025 18:26:18 +0100 Subject: [PATCH 06/28] docs: Add link to UV docs --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d6a3342b..51609d5f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,7 +26,7 @@ You can run these checks locally by installing [prefligit](https://github.com/j1 ```bash -# Requires UV: +# Requires UV: https://docs.astral.sh/uv/getting-started/installation/ # Mac/linux: curl -LsSf https://astral.sh/uv/install.sh | sh # Windows: powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" From 3c320f6d6ef73706604b196a9623231101e0d5fc Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Wed, 2 Jul 2025 18:34:55 +0100 Subject: [PATCH 07/28] docs: Fix code examples in style guide --- docs/development/code_style.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/development/code_style.md b/docs/development/code_style.md index 6051c787..7fa9d6bb 100644 --- a/docs/development/code_style.md +++ b/docs/development/code_style.md @@ -181,10 +181,10 @@ debug!( **Always use closure guards instead of implicitly dropped guards.** This makes lock scopes explicit and helps prevent deadlocks. -Use the `WithLock` trait from `continuwuity-core::utils::with_lock`: +Use the `WithLock` trait from `core::utils::with_lock`: ```rs -use continuwuity_core::utils::with_lock::WithLock; +use conduwuit::utils::with_lock::WithLock; // Good - explicit closure guard shared_data.with_lock(|data| { @@ -205,7 +205,7 @@ shared_data.with_lock(|data| { For async contexts, use the async variant: ```rs -use continuwuity_core::utils::with_lock::WithLockAsync; +use conduwuit::utils::with_lock::WithLockAsync; // Good - async closure guard async_shared_data.with_lock(|data| { From 2655acf269543134eb980bc38f38403e432de0e1 Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Mon, 14 Jul 2025 19:48:46 +0100 Subject: [PATCH 08/28] docs: Improve grammar in deployment documentation --- docs/deploying/arch-linux.md | 4 +- docs/deploying/docker.md | 42 ++++++++-------- docs/deploying/freebsd.md | 4 +- docs/deploying/generic.md | 92 ++++++++++++++++++------------------ docs/deploying/kubernetes.md | 8 ++-- docs/deploying/nixos.md | 22 ++++----- 6 files changed, 86 insertions(+), 86 deletions(-) diff --git a/docs/deploying/arch-linux.md b/docs/deploying/arch-linux.md index 6e50410d..70067abc 100644 --- a/docs/deploying/arch-linux.md +++ b/docs/deploying/arch-linux.md @@ -1,5 +1,5 @@ # Continuwuity for Arch Linux -Continuwuity is available on the `archlinuxcn` repository and AUR, with the same package name `continuwuity`, which includes latest taggged version. The development version is available on AUR as `continuwuity-git` +Continuwuity is available in the `archlinuxcn` repository and AUR with the same package name `continuwuity`, which includes the latest tagged version. The development version is available on AUR as `continuwuity-git`. -Simply install the `continuwuity` package. Configure the service in `/etc/conduwuit/conduwuit.toml`, then enable/start the continuwuity.service. +Simply install the `continuwuity` package. Configure the service in `/etc/conduwuit/conduwuit.toml`, then enable and start the continuwuity.service. diff --git a/docs/deploying/docker.md b/docs/deploying/docker.md index 051ed89b..952f2c95 100644 --- a/docs/deploying/docker.md +++ b/docs/deploying/docker.md @@ -2,7 +2,7 @@ ## Docker -To run Continuwuity with Docker you can either build the image yourself or pull it +To run Continuwuity with Docker, you can either build the image yourself or pull it from a registry. ### Use a registry @@ -26,7 +26,7 @@ to pull it to your machine. ### Run -When you have the image you can simply run it with +When you have the image, you can simply run it with ```bash docker run -d -p 8448:6167 \ @@ -36,7 +36,7 @@ docker run -d -p 8448:6167 \ --name continuwuity $LINK ``` -or you can use [docker compose](#docker-compose). +or you can use [Docker Compose](#docker-compose). The `-d` flag lets the container run in detached mode. You may supply an optional `continuwuity.toml` config file, the example config can be found @@ -46,15 +46,15 @@ using env vars. For an overview of possible values, please take a look at the [`docker-compose.yml`](docker-compose.yml) file. If you just want to test Continuwuity for a short time, you can use the `--rm` -flag, which will clean up everything related to your container after you stop +flag, which cleans up everything related to your container after you stop it. ### Docker-compose -If the `docker run` command is not for you or your setup, you can also use one +If the `docker run` command is not suitable for you or your setup, you can also use one of the provided `docker-compose` files. -Depending on your proxy setup, you can use one of the following files; +Depending on your proxy setup, you can use one of the following files: - If you already have a `traefik` instance set up, use [`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml) @@ -65,7 +65,7 @@ Depending on your proxy setup, you can use one of the following files; `example.com` placeholders with your own domain - For any other reverse proxy, use [`docker-compose.yml`](docker-compose.yml) -When picking the traefik-related compose file, rename it so it matches +When picking the Traefik-related compose file, rename it to `docker-compose.yml`, and rename the override file to `docker-compose.override.yml`. Edit the latter with the values you want for your server. @@ -77,18 +77,18 @@ create the `caddy` network before spinning up the containers: docker network create caddy ``` -After that, you can rename it so it matches `docker-compose.yml` and spin up the +After that, you can rename it to `docker-compose.yml` and spin up the containers! Additional info about deploying Continuwuity can be found [here](generic.md). ### Build -Official Continuwuity images are built using **Docker Buildx** and the Dockerfile found at [`docker/Dockerfile`][dockerfile-path]. This approach uses common Docker tooling and enables multi-platform builds efficiently. +Official Continuwuity images are built using **Docker Buildx** and the Dockerfile found at [`docker/Dockerfile`][dockerfile-path]. This approach uses common Docker tooling and enables efficient multi-platform builds. -The resulting images are broadly compatible with Docker and other container runtimes like Podman or containerd. +The resulting images are widely compatible with Docker and other container runtimes like Podman or containerd. -The images *do not contain a shell*. They contain only the Continuwuity binary, required libraries, TLS certificates and metadata. Please refer to the [`docker/Dockerfile`][dockerfile-path] for the specific details of the image composition. +The images *do not contain a shell*. They contain only the Continuwuity binary, required libraries, TLS certificates, and metadata. Please refer to the [`docker/Dockerfile`][dockerfile-path] for the specific details of the image composition. To build an image locally using Docker Buildx, you can typically run a command like: @@ -109,8 +109,8 @@ Refer to the Docker Buildx documentation for more advanced build options. ### Run -If you already have built the image or want to use one from the registries, you -can just start the container and everything else in the compose file in detached +If you have already built the image or want to use one from the registries, you +can start the container and everything else in the compose file in detached mode with: ```bash @@ -121,20 +121,20 @@ docker compose up -d ### Use Traefik as Proxy -As a container user, you probably know about Traefik. It is a easy to use -reverse proxy for making containerized app and services available through the +As a container user, you probably know about Traefik. It is an easy-to-use +reverse proxy for making containerized apps and services available through the web. With the two provided files, [`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml) (or [`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml)) and [`docker-compose.override.yml`](docker-compose.override.yml), it is equally easy -to deploy and use Continuwuity, with a little caveat. If you already took a look at -the files, then you should have seen the `well-known` service, and that is the -little caveat. Traefik is simply a proxy and loadbalancer and is not able to -serve any kind of content, but for Continuwuity to federate, we need to either -expose ports `443` and `8448` or serve two endpoints `.well-known/matrix/client` +to deploy and use Continuwuity, with a small caveat. If you have already looked at +the files, you should have seen the `well-known` service, which is the +small caveat. Traefik is simply a proxy and load balancer and cannot +serve any kind of content. For Continuwuity to federate, we need to either +expose ports `443` and `8448` or serve two endpoints: `.well-known/matrix/client` and `.well-known/matrix/server`. -With the service `well-known` we use a single `nginx` container that will serve +With the service `well-known`, we use a single `nginx` container that serves those two files. ## Voice communication diff --git a/docs/deploying/freebsd.md b/docs/deploying/freebsd.md index 3764ffa8..c637e0d9 100644 --- a/docs/deploying/freebsd.md +++ b/docs/deploying/freebsd.md @@ -1,5 +1,5 @@ # Continuwuity for FreeBSD -Continuwuity at the moment does not provide FreeBSD builds or have FreeBSD packaging, however Continuwuity does build and work on FreeBSD using the system-provided RocksDB. +Continuwuity currently does not provide FreeBSD builds or FreeBSD packaging. However, Continuwuity does build and work on FreeBSD using the system-provided RocksDB. -Contributions for getting Continuwuity packaged are welcome. +Contributions to get Continuwuity packaged for FreeBSD are welcome. diff --git a/docs/deploying/generic.md b/docs/deploying/generic.md index cfa9b32f..ac7f60c3 100644 --- a/docs/deploying/generic.md +++ b/docs/deploying/generic.md @@ -13,31 +13,31 @@ You may simply download the binary that fits your machine architecture (x86_64 or aarch64). Run `uname -m` to see what you need. -Prebuilt fully static musl binaries can be downloaded from the latest tagged +You can download prebuilt fully static musl binaries from the latest tagged release [here](https://forgejo.ellis.link/continuwuation/continuwuity/releases/latest) or -`main` CI branch workflow artifact output. These also include Debian/Ubuntu +from the `main` CI branch workflow artifact output. These also include Debian/Ubuntu packages. -These can be curl'd directly from. `ci-bins` are CI workflow binaries by commit +You can download these directly using curl. The `ci-bins` are CI workflow binaries organized by commit hash/revision, and `releases` are tagged releases. Sort by descending last -modified for the latest. +modified date to find the latest. These binaries have jemalloc and io_uring statically linked and included with them, so no additional dynamic dependencies need to be installed. -For the **best** performance; if using an `x86_64` CPU made in the last ~15 years, -we recommend using the `-haswell-` optimised binaries. This sets -`-march=haswell` which is the most compatible and highest performance with -optimised binaries. The database backend, RocksDB, most benefits from this as it -will then use hardware accelerated CRC32 hashing/checksumming which is critical +For the **best** performance: if you are using an `x86_64` CPU made in the last ~15 years, +we recommend using the `-haswell-` optimized binaries. These set +`-march=haswell`, which provides the most compatible and highest performance with +optimized binaries. The database backend, RocksDB, benefits most from this as it +uses hardware-accelerated CRC32 hashing/checksumming, which is critical for performance. ### Compiling Alternatively, you may compile the binary yourself. We recommend using -Nix (or [Lix](https://lix.systems)) to build Continuwuity as this has the most -guaranteed reproducibiltiy and easiest to get a build environment and output -going. This also allows easy cross-compilation. +Nix (or [Lix](https://lix.systems)) to build Continuwuity as this provides the most +guaranteed reproducibility and makes it easiest to set up a build environment and generate +output. This approach also allows for easy cross-compilation. You can run the `nix build -L .#static-x86_64-linux-musl-all-features` or `nix build -L .#static-aarch64-linux-musl-all-features` commands based @@ -53,9 +53,9 @@ You can build Continuwuity using `cargo build --release --all-features` ## Adding a Continuwuity user -While Continuwuity can run as any user it is better to use dedicated users for -different services. This also allows you to make sure that the file permissions -are correctly set up. +While Continuwuity can run as any user, it is better to use dedicated users for +different services. This also ensures that the file permissions +are set up correctly. In Debian, you can use this command to create a Continuwuity user: @@ -71,18 +71,18 @@ sudo useradd -r --shell /usr/bin/nologin --no-create-home continuwuity ## Forwarding ports in the firewall or the router -Matrix's default federation port is port 8448, and clients must be using port 443. -If you would like to use only port 443, or a different port, you will need to setup -delegation. Continuwuity has config options for doing delegation, or you can configure -your reverse proxy to manually serve the necessary JSON files to do delegation +Matrix's default federation port is 8448, and clients must use port 443. +If you would like to use only port 443 or a different port, you will need to set up +delegation. Continuwuity has configuration options for delegation, or you can configure +your reverse proxy to manually serve the necessary JSON files for delegation (see the `[global.well_known]` config section). If Continuwuity runs behind a router or in a container and has a different public -IP address than the host system these public ports need to be forwarded directly -or indirectly to the port mentioned in the config. +IP address than the host system, you need to forward these public ports directly +or indirectly to the port mentioned in the configuration. -Note for NAT users; if you have trouble connecting to your server from the inside -of your network, you need to research your router and see if it supports "NAT +Note for NAT users: if you have trouble connecting to your server from inside +your network, check if your router supports "NAT hairpinning" or "NAT loopback". If your router does not support this feature, you need to research doing local @@ -92,19 +92,19 @@ on the network level, consider something like NextDNS or Pi-Hole. ## Setting up a systemd service -Two example systemd units for Continuwuity can be found +You can find two example systemd units for Continuwuity [on the configuration page](../configuration/examples.md#debian-systemd-unit-file). -You may need to change the `ExecStart=` path to where you placed the Continuwuity -binary if it is not `/usr/bin/conduwuit`. +You may need to change the `ExecStart=` path to match where you placed the Continuwuity +binary if it is not in `/usr/bin/conduwuit`. On systems where rsyslog is used alongside journald (i.e. Red Hat-based distros and OpenSUSE), put `$EscapeControlCharactersOnReceive off` inside `/etc/rsyslog.conf` to allow color in logs. -If you are using a different `database_path` other than the systemd unit +If you are using a different `database_path` than the systemd unit's configured default `/var/lib/conduwuit`, you need to add your path to the -systemd unit's `ReadWritePaths=`. This can be done by either directly editing -`conduwuit.service` and reloading systemd, or running `systemctl edit conduwuit.service` +systemd unit's `ReadWritePaths=`. You can do this by either directly editing +`conduwuit.service` and reloading systemd, or by running `systemctl edit conduwuit.service` and entering the following: ``` @@ -114,8 +114,8 @@ ReadWritePaths=/path/to/custom/database/path ## Creating the Continuwuity configuration file -Now we need to create the Continuwuity's config file in -`/etc/continuwuity/continuwuity.toml`. The example config can be found at +Now you need to create the Continuwuity configuration file in +`/etc/continuwuity/continuwuity.toml`. You can find an example configuration at [conduwuit-example.toml](../configuration/examples.md). **Please take a moment to read the config. You need to change at least the @@ -125,8 +125,8 @@ RocksDB is the only supported database backend. ## Setting the correct file permissions -If you are using a dedicated user for Continuwuity, you will need to allow it to -read the config. To do that you can run this: +If you are using a dedicated user for Continuwuity, you need to allow it to +read the configuration. To do this, run: ```bash sudo chown -R root:root /etc/conduwuit @@ -143,13 +143,13 @@ sudo chmod 700 /var/lib/conduwuit/ ## Setting up the Reverse Proxy -We recommend Caddy as a reverse proxy, as it is trivial to use, handling TLS certificates, reverse proxy headers, etc transparently with proper defaults. +We recommend Caddy as a reverse proxy because it is trivial to use and handles TLS certificates, reverse proxy headers, etc. transparently with proper defaults. For other software, please refer to their respective documentation or online guides. ### Caddy After installing Caddy via your preferred method, create `/etc/caddy/conf.d/conduwuit_caddyfile` -and enter this (substitute for your server name). +and enter the following (substitute your actual server name): ```caddyfile your.server.name, your.server.name:8448 { @@ -168,9 +168,9 @@ sudo systemctl enable --now caddy ### Other Reverse Proxies -As we would prefer our users to use Caddy, we will not provide configuration files for other proxys. +As we prefer our users to use Caddy, we do not provide configuration files for other proxies. -You will need to reverse proxy everything under following routes: +You will need to reverse proxy everything under the following routes: - `/_matrix/` - core Matrix C-S and S-S APIs - `/_conduwuit/` and/or `/_continuwuity/` - ad-hoc Continuwuity routes such as `/local_user_count` and `/server_version` @@ -193,16 +193,16 @@ Examples of delegation: For Apache and Nginx there are many examples available online. -Lighttpd is not supported as it seems to mess with the `X-Matrix` Authorization -header, making federation non-functional. If a workaround is found, feel free to share to get it added to the documentation here. +Lighttpd is not supported as it appears to interfere with the `X-Matrix` Authorization +header, making federation non-functional. If you find a workaround, please share it so we can add it to this documentation. -If using Apache, you need to use `nocanon` in your `ProxyPass` directive to prevent httpd from messing with the `X-Matrix` header (note that Apache isn't very good as a general reverse proxy and we discourage the usage of it if you can). +If using Apache, you need to use `nocanon` in your `ProxyPass` directive to prevent httpd from interfering with the `X-Matrix` header (note that Apache is not ideal as a general reverse proxy, so we discourage using it if alternatives are available). -If using Nginx, you need to give Continuwuity the request URI using `$request_uri`, or like so: +If using Nginx, you need to pass the request URI to Continuwuity using `$request_uri`, like this: - `proxy_pass http://127.0.0.1:6167$request_uri;` - `proxy_pass http://127.0.0.1:6167;` -Nginx users need to increase `client_max_body_size` (default is 1M) to match +Nginx users need to increase the `client_max_body_size` setting (default is 1M) to match the `max_request_size` defined in conduwuit.toml. ## You're done @@ -222,7 +222,7 @@ sudo systemctl enable conduwuit ## How do I know it works? You can open [a Matrix client](https://matrix.org/ecosystem/clients), enter your -homeserver and try to register. +homeserver address, and try to register. You can also use these commands as a quick health check (replace `your.server.name`). @@ -237,10 +237,10 @@ curl https://your.server.name:8448/_conduwuit/server_version curl https://your.server.name:8448/_matrix/federation/v1/version ``` -- To check if your server can talk with other homeservers, you can use the +- To check if your server can communicate with other homeservers, use the [Matrix Federation Tester](https://federationtester.matrix.org/). If you can -register but cannot join federated rooms check your config again and also check -if the port 8448 is open and forwarded correctly. +register but cannot join federated rooms, check your configuration and verify +that port 8448 is open and forwarded correctly. # What's next? diff --git a/docs/deploying/kubernetes.md b/docs/deploying/kubernetes.md index 0cbfbbc0..b599a73e 100644 --- a/docs/deploying/kubernetes.md +++ b/docs/deploying/kubernetes.md @@ -1,9 +1,9 @@ # Continuwuity for Kubernetes Continuwuity doesn't support horizontal scalability or distributed loading -natively, however a community maintained Helm Chart is available here to run -conduwuit on Kubernetes: +natively. However, a community-maintained Helm Chart is available here to run +Continuwuity on Kubernetes: -This should be compatible with continuwuity, but you will need to change the image reference. +This should be compatible with Continuwuity, but you will need to change the image reference. -Should changes need to be made, please reach out to the maintainer as this is not maintained/controlled by the Continuwuity maintainers. +If changes need to be made, please reach out to the maintainer, as this is not maintained or controlled by the Continuwuity maintainers. diff --git a/docs/deploying/nixos.md b/docs/deploying/nixos.md index 2fdcbe5c..c097f075 100644 --- a/docs/deploying/nixos.md +++ b/docs/deploying/nixos.md @@ -1,6 +1,6 @@ # Continuwuity for NixOS -Continuwuity can be acquired by Nix (or [Lix][lix]) from various places: +You can acquire Continuwuity with Nix (or [Lix][lix]) from various places: * The `flake.nix` at the root of the repo * The `default.nix` at the root of the repo @@ -9,25 +9,25 @@ Continuwuity can be acquired by Nix (or [Lix][lix]) from various places: ### NixOS module The `flake.nix` and `default.nix` do not currently provide a NixOS module (contributions -welcome!), so [`services.matrix-conduit`][module] from Nixpkgs can be used to configure +welcome!), so you can use [`services.matrix-conduit`][module] from Nixpkgs to configure Continuwuity. ### Conduit NixOS Config Module and SQLite Beware! The [`services.matrix-conduit`][module] module defaults to SQLite as a database backend. Continuwuity dropped SQLite support in favor of exclusively supporting the much faster RocksDB. -Make sure that you are using the RocksDB backend before migrating! +Make sure you are using the RocksDB backend before migrating! -There is a [tool to migrate a Conduit SQLite database to +There is a [tool to migrate a Conduit SQLite database to RocksDB](https://github.com/ShadowJonathan/conduit_toolbox/). -If you want to run the latest code, you should get Continuwuity from the `flake.nix` +If you want to run the latest code, get Continuwuity from the `flake.nix` or `default.nix` and set [`services.matrix-conduit.package`][package] appropriately to use Continuwuity instead of Conduit. ### UNIX sockets -Due to the lack of a Continuwuity NixOS module, when using the `services.matrix-conduit` module +Due to the lack of a Continuwuity NixOS module, when using the `services.matrix-conduit` module, a workaround like the one below is necessary to use UNIX sockets. This is because the UNIX socket option does not exist in Conduit, and the module forcibly sets the `address` and `port` config options. @@ -44,21 +44,21 @@ options.services.matrix-conduit.settings = lib.mkOption { ``` Additionally, the [`matrix-conduit` systemd unit][systemd-unit] in the module does not allow -the `AF_UNIX` socket address family in their systemd unit's `RestrictAddressFamilies=` which -disallows the namespace from accessing or creating UNIX sockets and has to be enabled like so: +the `AF_UNIX` socket address family in its systemd unit's `RestrictAddressFamilies=`. This +disallows the namespace from accessing or creating UNIX sockets and must be enabled like this: ```nix systemd.services.conduit.serviceConfig.RestrictAddressFamilies = [ "AF_UNIX" ]; ``` -Even though those workarounds are feasible a Continuwuity NixOS configuration module, developed and +Although these workarounds are feasible, a dedicated Continuwuity NixOS configuration module, developed and published by the community, would be appreciated. ### jemalloc and hardened profile Continuwuity uses jemalloc by default. This may interfere with the [`hardened.nix` profile][hardened.nix] -due to them using `scudo` by default. You must either disable/hide `scudo` from Continuwuity, or -disable jemalloc like so: +because it uses `scudo` by default. You must either disable/hide `scudo` from Continuwuity or +disable jemalloc like this: ```nix let From 4a5b122d776c17b82ea86e4d23debc2b3bf13553 Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Mon, 14 Jul 2025 19:54:44 +0100 Subject: [PATCH 09/28] docs: Improve grammar in Debian package --- debian/README.md | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/debian/README.md b/debian/README.md index 4a8e58d2..b605b198 100644 --- a/debian/README.md +++ b/debian/README.md @@ -1,29 +1,23 @@ # Continuwuity for Debian -Information about downloading and deploying the Debian package. This may also be -referenced for other `apt`-based distros such as Ubuntu. +This document provides information about downloading and deploying the Debian package. You can also use this guide for other `apt`-based distributions such as Ubuntu. ### Installation -It is recommended to see the [generic deployment guide](../deploying/generic.md) -for further information if needed as usage of the Debian package is generally -related. +See the [generic deployment guide](../deploying/generic.md) for additional information about using the Debian package. -No `apt` repository is currently offered yet, it is in the works/development. +No `apt` repository is currently available. This feature is in development. ### Configuration -When installed, the example config is placed at `/etc/conduwuit/conduwuit.toml` -as the default config. The config mentions things required to be changed before -starting. +After installation, Continuwuity places the example configuration at `/etc/conduwuit/conduwuit.toml` as the default configuration file. The configuration file indicates which settings you must change before starting the service. -You can tweak more detailed settings by uncommenting and setting the config -options in `/etc/conduwuit/conduwuit.toml`. +You can customize additional settings by uncommenting and modifying the configuration options in `/etc/conduwuit/conduwuit.toml`. ### Running -The package uses the [`conduwuit.service`](../configuration/examples.md#example-systemd-unit-file) systemd unit file to start and stop Continuwuity. The binary is installed at `/usr/sbin/conduwuit`. +The package uses the [`conduwuit.service`](../configuration/examples.md#example-systemd-unit-file) systemd unit file to start and stop Continuwuity. The binary installs at `/usr/sbin/conduwuit`. -This package assumes by default that conduwuit will be placed behind a reverse proxy. The default config options apply (listening on `localhost` and TCP port `6167`). Matrix federation requires a valid domain name and TLS, so you will need to set up TLS certificates and renewal for it to work properly if you intend to federate. +By default, this package assumes that Continuwuity runs behind a reverse proxy. The default configuration options apply (listening on `localhost` and TCP port `6167`). Matrix federation requires a valid domain name and TLS. To federate properly, you must set up TLS certificates and certificate renewal. -Consult various online documentation and guides on setting up a reverse proxy and TLS. Caddy is documented at the [generic deployment guide](../deploying/generic.md#setting-up-the-reverse-proxy) as it's the easiest and most user friendly. +For information about setting up a reverse proxy and TLS, consult online documentation and guides. The [generic deployment guide](../deploying/generic.md#setting-up-the-reverse-proxy) documents Caddy, which is the most user-friendly option for reverse proxy configuration. From 13de0ac822dd19fdc51e86a3a68ad3f559abb6e6 Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Mon, 14 Jul 2025 20:12:01 +0100 Subject: [PATCH 10/28] docs: Update and improve NixOS documentation Documentation now mentions the official package --- docs/deploying/kubernetes.md | 2 +- docs/deploying/nixos.md | 151 ++++++++++++++++++++++++----------- 2 files changed, 104 insertions(+), 49 deletions(-) diff --git a/docs/deploying/kubernetes.md b/docs/deploying/kubernetes.md index b599a73e..2750e3fb 100644 --- a/docs/deploying/kubernetes.md +++ b/docs/deploying/kubernetes.md @@ -2,7 +2,7 @@ Continuwuity doesn't support horizontal scalability or distributed loading natively. However, a community-maintained Helm Chart is available here to run -Continuwuity on Kubernetes: +conduwuit on Kubernetes: This should be compatible with Continuwuity, but you will need to change the image reference. diff --git a/docs/deploying/nixos.md b/docs/deploying/nixos.md index c097f075..29517416 100644 --- a/docs/deploying/nixos.md +++ b/docs/deploying/nixos.md @@ -1,75 +1,130 @@ # Continuwuity for NixOS -You can acquire Continuwuity with Nix (or [Lix][lix]) from various places: +NixOS packages Continuwuity as `matrix-continuwuity`. This package includes both the Continuwuity software and a dedicated NixOS module for configuration and deployment. -* The `flake.nix` at the root of the repo -* The `default.nix` at the root of the repo -* From Continuwuity's binary cache +## Installation methods -### NixOS module +You can acquire Continuwuity with Nix (or [Lix][lix]) from these sources: -The `flake.nix` and `default.nix` do not currently provide a NixOS module (contributions -welcome!), so you can use [`services.matrix-conduit`][module] from Nixpkgs to configure -Continuwuity. +* Directly from Nixpkgs using the official package (`pkgs.matrix-continuwuity`) +* The `flake.nix` at the root of the Continuwuity repo +* The `default.nix` at the root of the Continuwuity repo -### Conduit NixOS Config Module and SQLite +## NixOS module -Beware! The [`services.matrix-conduit`][module] module defaults to SQLite as a database backend. -Continuwuity dropped SQLite support in favor of exclusively supporting the much faster RocksDB. -Make sure you are using the RocksDB backend before migrating! +Continuwuity now has an official NixOS module that simplifies configuration and deployment. The module is available in Nixpkgs as `services.matrix-continuwuity` from NixOS 25.05. -There is a [tool to migrate a Conduit SQLite database to -RocksDB](https://github.com/ShadowJonathan/conduit_toolbox/). +Here's a basic example of how to use the module: -If you want to run the latest code, get Continuwuity from the `flake.nix` -or `default.nix` and set [`services.matrix-conduit.package`][package] -appropriately to use Continuwuity instead of Conduit. +```nix +{ config, pkgs, ... }: + +{ + services.matrix-continuwuity = { + enable = true; + settings = { + global = { + server_name = "example.com"; + # Listening on localhost by default + # address and port are handled automatically + allow_registration = false; + allow_encryption = true; + allow_federation = true; + trusted_servers = [ "matrix.org" ]; + }; + }; + }; +} +``` + +### Available options + +The NixOS module provides these configuration options: + +- `enable`: Enable the Continuwuity service +- `user`: The user to run Continuwuity as (defaults to "continuwuity") +- `group`: The group to run Continuwuity as (defaults to "continuwuity") +- `extraEnvironment`: Extra environment variables to pass to the Continuwuity server +- `package`: The Continuwuity package to use +- `settings`: The Continuwuity configuration (in TOML format) + +Use the `settings` option to configure Continuwuity itself. See the [example configuration file](../configuration/examples.md#example-configuration) for all available options. ### UNIX sockets -Due to the lack of a Continuwuity NixOS module, when using the `services.matrix-conduit` module, -a workaround like the one below is necessary to use UNIX sockets. This is because the UNIX -socket option does not exist in Conduit, and the module forcibly sets the `address` and -`port` config options. +The NixOS module natively supports UNIX sockets through the `global.unix_socket_path` option. When using UNIX sockets, set `global.address` to `null`: ```nix -options.services.matrix-conduit.settings = lib.mkOption { - apply = old: old // ( - if (old.global ? "unix_socket_path") - then { global = builtins.removeAttrs old.global [ "address" "port" ]; } - else { } - ); +services.matrix-continuwuity = { + enable = true; + settings = { + global = { + server_name = "example.com"; + address = null; # Must be null when using unix_socket_path + unix_socket_path = "/run/continuwuity/continuwuity.sock"; + unix_socket_perms = 660; # Default permissions for the socket + # ... + }; + }; }; - ``` -Additionally, the [`matrix-conduit` systemd unit][systemd-unit] in the module does not allow -the `AF_UNIX` socket address family in its systemd unit's `RestrictAddressFamilies=`. This -disallows the namespace from accessing or creating UNIX sockets and must be enabled like this: +The module automatically sets the correct `RestrictAddressFamilies` in the systemd service configuration to allow access to UNIX sockets. -```nix -systemd.services.conduit.serviceConfig.RestrictAddressFamilies = [ "AF_UNIX" ]; -``` +### RocksDB database -Although these workarounds are feasible, a dedicated Continuwuity NixOS configuration module, developed and -published by the community, would be appreciated. +Continuwuity exclusively uses RocksDB as its database backend. The system configures the database path automatically to `/var/lib/continuwuity/` and you cannot change it due to the service's reliance on systemd's StateDir. + +If you're migrating from Conduit with SQLite, use this [tool to migrate a Conduit SQLite database to RocksDB](https://github.com/ShadowJonathan/conduit_toolbox/). ### jemalloc and hardened profile -Continuwuity uses jemalloc by default. This may interfere with the [`hardened.nix` profile][hardened.nix] -because it uses `scudo` by default. You must either disable/hide `scudo` from Continuwuity or -disable jemalloc like this: +Continuwuity uses jemalloc by default. This may interfere with the [`hardened.nix` profile][hardened.nix] because it uses `scudo` by default. Either disable/hide `scudo` from Continuwuity or disable jemalloc like this: ```nix -let - conduwuit = pkgs.unstable.conduwuit.override { - enableJemalloc = false; - }; -in +services.matrix-continuwuity = { + enable = true; + package = pkgs.matrix-continuwuity.override { + enableJemalloc = false; + }; + # ... +}; +``` + +## Upgrading from Conduit + +If you previously used Conduit with the `services.matrix-conduit` module: + +1. Ensure your Conduit uses the RocksDB backend, or migrate from SQLite using the [migration tool](https://github.com/ShadowJonathan/conduit_toolbox/) +2. Switch to the new module by changing `services.matrix-conduit` to `services.matrix-continuwuity` in your configuration +3. Update any custom configuration to match the new module's structure + +## Reverse proxy configuration + +You'll need to set up a reverse proxy (like nginx or caddy) to expose Continuwuity to the internet. Configure your reverse proxy to forward requests to `/_matrix` on port 443 and 8448 to your Continuwuity instance. + +Here's an example nginx configuration: + +```nginx +server { + listen 443 ssl; + listen [::]:443 ssl; + listen 8448 ssl; + listen [::]:8448 ssl; + + server_name example.com; + + # SSL configuration here... + + location /_matrix/ { + proxy_pass http://127.0.0.1:6167$request_uri; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} ``` [lix]: https://lix.systems/ -[module]: https://search.nixos.org/options?channel=unstable&query=services.matrix-conduit -[package]: https://search.nixos.org/options?channel=unstable&query=services.matrix-conduit.package -[hardened.nix]: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/hardened.nix#L22 -[systemd-unit]: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/matrix/conduit.nix#L132 +[hardened.nix]: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/hardened.nix From a89ceb93d82fc27026dba548db4dd08989232323 Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Mon, 14 Jul 2025 20:24:29 +0100 Subject: [PATCH 11/28] docs: Update Docker and generic instructions Add instructions for proxying .well-known to Continuwuity in with Traefik. Clarify and expand build instructions in generic deployment, separating Rust toolchain and Nix approaches. --- docs/deploying/docker.md | 2 ++ docs/deploying/generic.md | 25 +++++++++++++++---------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/docs/deploying/docker.md b/docs/deploying/docker.md index 952f2c95..a928d081 100644 --- a/docs/deploying/docker.md +++ b/docs/deploying/docker.md @@ -137,6 +137,8 @@ and `.well-known/matrix/server`. With the service `well-known`, we use a single `nginx` container that serves those two files. +Alternatively, you can use Continuwuity's built-in delegation file capability. Set up the delegation files in the configuration file, and then proxy paths under `/.well-known/matrix` to continuwuity. For example, the label ``traefik.http.routers.continuwuity.rule=(Host(`matrix.ellis.link`) || (Host(`ellis.link`) && PathPrefix(`/.well-known/matrix`)))`` does this for the domain `ellis.link`. + ## Voice communication See the [TURN](../turn.md) page. diff --git a/docs/deploying/generic.md b/docs/deploying/generic.md index ac7f60c3..3f9d1a16 100644 --- a/docs/deploying/generic.md +++ b/docs/deploying/generic.md @@ -34,10 +34,21 @@ for performance. ### Compiling -Alternatively, you may compile the binary yourself. We recommend using -Nix (or [Lix](https://lix.systems)) to build Continuwuity as this provides the most -guaranteed reproducibility and makes it easiest to set up a build environment and generate -output. This approach also allows for easy cross-compilation. +Alternatively, you may compile the binary yourself. + +### Building with the Rust toolchain + +If wanting to build using standard Rust toolchains, make sure you install: + +- (On linux) `liburing-dev` on the compiling machine, and `liburing` on the target host +- (On linux) `pkg-config` on the compiling machine to allow finding `liburing` +- A C++ compiler and (on linux) `libclang` for RocksDB + +You can build Continuwuity using `cargo build --release --all-features`. + +### Building with Nix + +If you prefer, you can use Nix (or [Lix](https://lix.systems)) to build Continuwuity. This provides improved reproducibility and makes it easy to set up a build environment and generate output. This approach also allows for easy cross-compilation. You can run the `nix build -L .#static-x86_64-linux-musl-all-features` or `nix build -L .#static-aarch64-linux-musl-all-features` commands based @@ -45,12 +56,6 @@ on architecture to cross-compile the necessary static binary located at `result/bin/conduwuit`. This is reproducible with the static binaries produced in our CI. -If wanting to build using standard Rust toolchains, make sure you install: -- `liburing-dev` on the compiling machine, and `liburing` on the target host -- LLVM and libclang for RocksDB - -You can build Continuwuity using `cargo build --release --all-features` - ## Adding a Continuwuity user While Continuwuity can run as any user, it is better to use dedicated users for From 0a8c13ffd2cb64dd068c58e9f0900ba39d552109 Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Tue, 15 Jul 2025 14:48:33 +0100 Subject: [PATCH 12/28] fix: Use boolean where expected in services Fixes https://forgejo.ellis.link/continuwuation/continuwuity/issues/905 --- arch/conduwuit.service | 2 +- debian/conduwuit.service | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/conduwuit.service b/arch/conduwuit.service index b66bc1da..f7100179 100644 --- a/arch/conduwuit.service +++ b/arch/conduwuit.service @@ -18,7 +18,7 @@ StandardInput=tty-force StandardOutput=tty StandardError=journal+console -Environment="CONTINUWUITY_LOG_TO_JOURNALD=1" +Environment="CONTINUWUITY_LOG_TO_JOURNALD=true" Environment="CONTINUWUITY_JOURNALD_IDENTIFIER=%N" TTYReset=yes diff --git a/debian/conduwuit.service b/debian/conduwuit.service index b95804d3..da78f09f 100644 --- a/debian/conduwuit.service +++ b/debian/conduwuit.service @@ -14,7 +14,7 @@ Type=notify Environment="CONTINUWUITY_CONFIG=/etc/conduwuit/conduwuit.toml" -Environment="CONTINUWUITY_LOG_TO_JOURNALD=1" +Environment="CONTINUWUITY_LOG_TO_JOURNALD=true" Environment="CONTINUWUITY_JOURNALD_IDENTIFIER=%N" ExecStart=/usr/sbin/conduwuit From 843e501902aa0f01f56d8aa5279c340bf82c8825 Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Wed, 16 Jul 2025 23:47:41 +0100 Subject: [PATCH 13/28] docs: Add section for testing TURN servers --- docs/turn.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/turn.md b/docs/turn.md index 5dba823c..cccb1cb3 100644 --- a/docs/turn.md +++ b/docs/turn.md @@ -68,3 +68,27 @@ documentation](https://github.com/coturn/coturn/blob/master/docker/coturn/README For security recommendations see Synapse's [Coturn documentation](https://element-hq.github.io/synapse/latest/turn-howto.html). + +### Testing + +To make sure turn credentials are being correctly served to clients, you can manually make a HTTP request to the turnServer endpoint. + +`curl "https:///_matrix/client/r0/voip/turnServer" -H 'Authorization: Bearer ' | jq` + +You should get a response like this: + +```json +{ + "username": "1752792167:@jade:example.com", + "password": "KjlDlawdPbU9mvP4bhdV/2c/h65=", + "uris": [ + "turns:coturn.example.com?transport=udp", + "turns:coturn.example.com?transport=tcp", + "turn:coturn.example.com?transport=udp", + "turn:coturn.example.com?transport=tcp" + ], + "ttl": 86400 +} +``` + +You can test these credentials work using [Trickle ICE](https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/) From 3b5d5dcefa4a7932b7d6bf4b801bbc1a6c1e341f Mon Sep 17 00:00:00 2001 From: Jacob Taylor Date: Fri, 25 Apr 2025 21:06:00 -0700 Subject: [PATCH 14/28] probably incorrectly delete support for non-standardized matrix srv record --- src/service/resolver/actual.rs | 37 ++++++++++++++++------------------ 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/service/resolver/actual.rs b/src/service/resolver/actual.rs index d23ef95a..52cd5d7d 100644 --- a/src/service/resolver/actual.rs +++ b/src/service/resolver/actual.rs @@ -306,28 +306,25 @@ impl super::Service { #[tracing::instrument(name = "srv", level = "debug", skip(self))] async fn query_srv_record(&self, hostname: &'_ str) -> Result> { - let hostnames = - [format!("_matrix-fed._tcp.{hostname}."), format!("_matrix._tcp.{hostname}.")]; + self.services.server.check_running()?; - for hostname in hostnames { - self.services.server.check_running()?; + debug!("querying SRV for {hostname:?}"); - debug!("querying SRV for {hostname:?}"); - let hostname = hostname.trim_end_matches('.'); - match self.resolver.resolver.srv_lookup(hostname).await { - | Err(e) => Self::handle_resolve_error(&e, hostname)?, - | Ok(result) => { - return Ok(result.iter().next().map(|result| { - FedDest::Named( - result.target().to_string().trim_end_matches('.').to_owned(), - format!(":{}", result.port()) - .as_str() - .try_into() - .unwrap_or_else(|_| FedDest::default_port()), - ) - })); - }, - } + let hostname_suffix = format!("_matrix-fed._tcp.{hostname}."); + let hostname = hostname_suffix.trim_end_matches('.'); + match self.resolver.resolver.srv_lookup(hostname).await { + | Err(e) => Self::handle_resolve_error(&e, hostname)?, + | Ok(result) => { + return Ok(result.iter().next().map(|result| { + FedDest::Named( + result.target().to_string().trim_end_matches('.').to_owned(), + format!(":{}", result.port()) + .as_str() + .try_into() + .unwrap_or_else(|_| FedDest::default_port()), + ) + })); + }, } Ok(None) From ae8127d44b2eea6ff07c623694e139a4c973e431 Mon Sep 17 00:00:00 2001 From: Jacob Taylor Date: Wed, 14 May 2025 06:53:00 -0700 Subject: [PATCH 15/28] bump the number of allowed immutable memtables by 1, to allow for greater flood protection this should probably not be applied if you have rocksdb_atomic_flush = false (the default) --- src/database/engine/cf_opts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database/engine/cf_opts.rs b/src/database/engine/cf_opts.rs index cbbd1012..666f9f9e 100644 --- a/src/database/engine/cf_opts.rs +++ b/src/database/engine/cf_opts.rs @@ -29,7 +29,7 @@ fn descriptor_cf_options( set_table_options(&mut opts, &desc, cache)?; opts.set_min_write_buffer_number(1); - opts.set_max_write_buffer_number(2); + opts.set_max_write_buffer_number(3); opts.set_write_buffer_size(desc.write_size); opts.set_target_file_size_base(desc.file_size); From a62e658e6543f1f752536bd331c7fabdf7cf9ab3 Mon Sep 17 00:00:00 2001 From: Jacob Taylor Date: Sat, 21 Jun 2025 08:02:05 -0700 Subject: [PATCH 16/28] upgrade some settings to enable 5g in continuwuity enable converged 6g at the edge in continuwuity better stateinfo_cache_capacity default better roomid_spacehierarchy_cache_capacity make sender workers default better and clamp value to core count update sender workers documentation add more parallelism_scaled and make them public update 1 document --- conduwuit-example.toml | 6 +-- src/core/config/mod.rs | 75 +++++++++++++++++++------------------- src/service/sending/mod.rs | 12 ++---- 3 files changed, 43 insertions(+), 50 deletions(-) diff --git a/conduwuit-example.toml b/conduwuit-example.toml index bdc2f570..2c3721d0 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -1655,11 +1655,9 @@ #stream_amplification = 1024 # Number of sender task workers; determines sender parallelism. Default is -# '0' which means the value is determined internally, likely matching the -# number of tokio worker-threads or number of cores, etc. Override by -# setting a non-zero value. +# number of CPU cores. Override by setting a different value. # -#sender_workers = 0 +#sender_workers = 4 # Enables listener sockets; can be set to false to disable listening. This # option is intended for developer/diagnostic purposes only. diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index d93acd9b..17da6492 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -1889,12 +1889,10 @@ pub struct Config { pub stream_amplification: usize, /// Number of sender task workers; determines sender parallelism. Default is - /// '0' which means the value is determined internally, likely matching the - /// number of tokio worker-threads or number of cores, etc. Override by - /// setting a non-zero value. + /// '4'. Override by setting a different value. Values clamped 1 to core count. /// - /// default: 0 - #[serde(default)] + /// default: 4 + #[serde(default = "default_sender_workers")] pub sender_workers: usize, /// Enables listener sockets; can be set to false to disable listening. This @@ -2125,45 +2123,47 @@ fn default_database_backups_to_keep() -> i16 { 1 } fn default_db_write_buffer_capacity_mb() -> f64 { 48.0 + parallelism_scaled_f64(4.0) } -fn default_db_cache_capacity_mb() -> f64 { 128.0 + parallelism_scaled_f64(64.0) } +fn default_db_cache_capacity_mb() -> f64 { 512.0 + parallelism_scaled_f64(512.0) } -fn default_pdu_cache_capacity() -> u32 { parallelism_scaled_u32(10_000).saturating_add(100_000) } +fn default_pdu_cache_capacity() -> u32 { parallelism_scaled_u32(50_000).saturating_add(500_000) } fn default_cache_capacity_modifier() -> f64 { 1.0 } fn default_auth_chain_cache_capacity() -> u32 { - parallelism_scaled_u32(10_000).saturating_add(100_000) + parallelism_scaled_u32(50_000).saturating_add(500_000) } fn default_shorteventid_cache_capacity() -> u32 { - parallelism_scaled_u32(50_000).saturating_add(100_000) -} - -fn default_eventidshort_cache_capacity() -> u32 { - parallelism_scaled_u32(25_000).saturating_add(100_000) -} - -fn default_eventid_pdu_cache_capacity() -> u32 { - parallelism_scaled_u32(25_000).saturating_add(100_000) -} - -fn default_shortstatekey_cache_capacity() -> u32 { - parallelism_scaled_u32(10_000).saturating_add(100_000) -} - -fn default_statekeyshort_cache_capacity() -> u32 { - parallelism_scaled_u32(10_000).saturating_add(100_000) -} - -fn default_servernameevent_data_cache_capacity() -> u32 { parallelism_scaled_u32(100_000).saturating_add(500_000) } -fn default_stateinfo_cache_capacity() -> u32 { parallelism_scaled_u32(100) } +fn default_eventidshort_cache_capacity() -> u32 { + parallelism_scaled_u32(100_000).saturating_add(500_000) +} -fn default_roomid_spacehierarchy_cache_capacity() -> u32 { parallelism_scaled_u32(1000) } +fn default_eventid_pdu_cache_capacity() -> u32 { + parallelism_scaled_u32(50_000).saturating_add(500_000) +} -fn default_dns_cache_entries() -> u32 { 32768 } +fn default_shortstatekey_cache_capacity() -> u32 { + parallelism_scaled_u32(50_000).saturating_add(500_000) +} + +fn default_statekeyshort_cache_capacity() -> u32 { + parallelism_scaled_u32(50_000).saturating_add(500_000) +} + +fn default_servernameevent_data_cache_capacity() -> u32 { + parallelism_scaled_u32(200_000).saturating_add(500_000) +} + +fn default_stateinfo_cache_capacity() -> u32 { + parallelism_scaled_u32(500).clamp(100, 12000) } + +fn default_roomid_spacehierarchy_cache_capacity() -> u32 { + parallelism_scaled_u32(500).clamp(100, 12000) } + +fn default_dns_cache_entries() -> u32 { 327680 } fn default_dns_min_ttl() -> u64 { 60 * 180 } @@ -2352,14 +2352,13 @@ fn default_admin_log_capture() -> String { fn default_admin_room_tag() -> String { "m.server_notice".to_owned() } #[allow(clippy::as_conversions, clippy::cast_precision_loss)] -fn parallelism_scaled_f64(val: f64) -> f64 { val * (sys::available_parallelism() as f64) } +pub fn parallelism_scaled_f64(val: f64) -> f64 { val * (sys::available_parallelism() as f64) } -fn parallelism_scaled_u32(val: u32) -> u32 { - let val = val.try_into().expect("failed to cast u32 to usize"); - parallelism_scaled(val).try_into().unwrap_or(u32::MAX) -} +pub fn parallelism_scaled_u32(val: u32) -> u32 { val.saturating_mul(sys::available_parallelism() as u32) } -fn parallelism_scaled(val: usize) -> usize { val.saturating_mul(sys::available_parallelism()) } +pub fn parallelism_scaled_i32(val: i32) -> i32 { val.saturating_mul(sys::available_parallelism() as i32) } + +pub fn parallelism_scaled(val: usize) -> usize { val.saturating_mul(sys::available_parallelism()) } fn default_trusted_server_batch_size() -> usize { 256 } @@ -2379,6 +2378,8 @@ fn default_stream_width_scale() -> f32 { 1.0 } fn default_stream_amplification() -> usize { 1024 } +fn default_sender_workers() -> usize { 4 } + fn default_client_receive_timeout() -> u64 { 75 } fn default_client_request_timeout() -> u64 { 180 } diff --git a/src/service/sending/mod.rs b/src/service/sending/mod.rs index 08ca7010..ce687551 100644 --- a/src/service/sending/mod.rs +++ b/src/service/sending/mod.rs @@ -401,16 +401,10 @@ impl Service { fn num_senders(args: &crate::Args<'_>) -> usize { const MIN_SENDERS: usize = 1; - // Limit the number of senders to the number of workers threads or number of - // cores, conservatively. - let max_senders = args - .server - .metrics - .num_workers() - .min(available_parallelism()); + // Limit the maximum number of senders to the number of cores. + let max_senders = available_parallelism(); - // If the user doesn't override the default 0, this is intended to then default - // to 1 for now as multiple senders is experimental. + // default is 4 senders. clamp between 1 and core count. args.server .config .sender_workers From bb4b625f631cd8370f50a6ac60b543a549f36afa Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Sat, 7 Jun 2025 00:46:55 +0100 Subject: [PATCH 17/28] fix an auth rule not applying correctly --- src/core/matrix/state_res/event_auth.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/core/matrix/state_res/event_auth.rs b/src/core/matrix/state_res/event_auth.rs index 5c36ce03..0b5b72d7 100644 --- a/src/core/matrix/state_res/event_auth.rs +++ b/src/core/matrix/state_res/event_auth.rs @@ -255,6 +255,16 @@ where }, | Some(e) => e, }; + // just re-check 1.2 to work around a bug + let Some(room_id_server_name) = incoming_event.room_id().server_name() else { + warn!("room ID has no servername"); + return Ok(false); + }; + + if room_id_server_name != sender.server_name() { + warn!("servername of room ID does not match servername of m.room.create sender"); + return Ok(false); + } if incoming_event.room_id() != room_create_event.room_id() { warn!("room_id of incoming event does not match room_id of m.room.create event"); From bf3dd254e858eee6cbe433d6aacafe5ef6f37a94 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Sat, 7 Jun 2025 00:55:03 +0100 Subject: [PATCH 18/28] Note about ruma#2064 in TODO --- src/core/matrix/state_res/event_auth.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/matrix/state_res/event_auth.rs b/src/core/matrix/state_res/event_auth.rs index 0b5b72d7..40c32e03 100644 --- a/src/core/matrix/state_res/event_auth.rs +++ b/src/core/matrix/state_res/event_auth.rs @@ -217,8 +217,9 @@ where } /* - // TODO: In the past this code caused problems federating with synapse, maybe this has been - // resolved already. Needs testing. + // TODO: In the past this code was commented as it caused problems with Synapse. This is no + // longer the case. This needs to be implemented. + // See also: https://github.com/ruma/ruma/pull/2064 // // 2. Reject if auth_events // a. auth_events cannot have duplicate keys since it's a BTree From e147f0f2745abdfc0a40b48f8ec53b222d266cc4 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Tue, 10 Jun 2025 22:33:31 +0100 Subject: [PATCH 19/28] Kick up a fuss when m.room.create is unfindable --- src/core/matrix/state_res/event_auth.rs | 4 ++-- src/core/matrix/state_res/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/matrix/state_res/event_auth.rs b/src/core/matrix/state_res/event_auth.rs index 40c32e03..31c660ed 100644 --- a/src/core/matrix/state_res/event_auth.rs +++ b/src/core/matrix/state_res/event_auth.rs @@ -30,7 +30,7 @@ use super::{ }, room_version::RoomVersion, }; -use crate::{debug, error, trace, warn}; +use crate::{debug, err_log, error, trace, warn}; // FIXME: field extracting could be bundled for `content` #[derive(Deserialize)] @@ -251,7 +251,7 @@ where let room_create_event = match room_create_event { | None => { - warn!("no m.room.create event in auth chain"); + error!("no m.room.create event in auth chain for {}!", incoming_event.event_id()); return Ok(false); }, | Some(e) => e, diff --git a/src/core/matrix/state_res/mod.rs b/src/core/matrix/state_res/mod.rs index ce9d9276..e721e14c 100644 --- a/src/core/matrix/state_res/mod.rs +++ b/src/core/matrix/state_res/mod.rs @@ -753,7 +753,7 @@ where } } } - // Did not find a power level event so we default to zero + warn!("could not find a power event in the mainline map, defaulting to zero depth"); Ok(0) } From a61fd287ef7d1937e4ce0d3b51dbed6b31966cc4 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Tue, 10 Jun 2025 23:00:09 +0100 Subject: [PATCH 20/28] Fix room ID check --- src/core/matrix/state_res/event_auth.rs | 11 +++++++---- src/service/rooms/event_handler/handle_outlier_pdu.rs | 5 +---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/matrix/state_res/event_auth.rs b/src/core/matrix/state_res/event_auth.rs index 31c660ed..de4d20e1 100644 --- a/src/core/matrix/state_res/event_auth.rs +++ b/src/core/matrix/state_res/event_auth.rs @@ -30,7 +30,7 @@ use super::{ }, room_version::RoomVersion, }; -use crate::{debug, err_log, error, trace, warn}; +use crate::{debug, error, trace, warn}; // FIXME: field extracting could be bundled for `content` #[derive(Deserialize)] @@ -251,7 +251,7 @@ where let room_create_event = match room_create_event { | None => { - error!("no m.room.create event in auth chain for {}!", incoming_event.event_id()); + error!("no m.room.create event found for {}!", incoming_event.event_id()); return Ok(false); }, | Some(e) => e, @@ -262,8 +262,11 @@ where return Ok(false); }; - if room_id_server_name != sender.server_name() { - warn!("servername of room ID does not match servername of m.room.create sender"); + if room_id_server_name != room_create_event.sender().server_name() { + warn!( + "servername of room ID origin ({}) does not match servername of m.room.create sender ({})", + room_id_server_name, + room_create_event.sender().server_name()); return Ok(false); } diff --git a/src/service/rooms/event_handler/handle_outlier_pdu.rs b/src/service/rooms/event_handler/handle_outlier_pdu.rs index d79eed77..fad9ac74 100644 --- a/src/service/rooms/event_handler/handle_outlier_pdu.rs +++ b/src/service/rooms/event_handler/handle_outlier_pdu.rs @@ -122,10 +122,7 @@ where } // The original create event must be in the auth events - if !matches!( - auth_events.get(&(StateEventType::RoomCreate, String::new().into())), - Some(_) | None - ) { + if !auth_events.contains_key(&(StateEventType::RoomCreate, String::new().into())) { return Err!(Request(InvalidParam("Incoming event refers to wrong create event."))); } From 5dafe80527ba3c26775985eb328e3621f5957e3a Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Wed, 11 Jun 2025 01:27:25 +0100 Subject: [PATCH 21/28] more logs --- src/core/matrix/state_res/event_auth.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/core/matrix/state_res/event_auth.rs b/src/core/matrix/state_res/event_auth.rs index de4d20e1..fc1119de 100644 --- a/src/core/matrix/state_res/event_auth.rs +++ b/src/core/matrix/state_res/event_auth.rs @@ -13,6 +13,7 @@ use ruma::{ power_levels::RoomPowerLevelsEventContent, third_party_invite::RoomThirdPartyInviteEventContent, }, + EventId, int, serde::{Base64, Raw}, }; @@ -21,7 +22,6 @@ use serde::{ de::{Error as _, IgnoredAny}, }; use serde_json::{from_str as from_json_str, value::RawValue as RawJsonValue}; - use super::{ Error, Event, Result, StateEventType, StateKey, TimelineEventType, power_levels::{ @@ -251,7 +251,14 @@ where let room_create_event = match room_create_event { | None => { - error!("no m.room.create event found for {}!", incoming_event.event_id()); + error!( + create_event = room_create_event.as_ref().map(Event::event_id).unwrap_or(<&EventId>::try_from("$unknown").unwrap()).as_str(), + power_levels = power_levels_event.as_ref().map(Event::event_id).unwrap_or(<&EventId>::try_from("$unknown").unwrap()).as_str(), + member_event = sender_member_event.as_ref().map(Event::event_id).unwrap_or(<&EventId>::try_from("$unknown").unwrap()).as_str(), + "no m.room.create event found for {} ({})!", + incoming_event.event_id().as_str(), + incoming_event.room_id().as_str() + ); return Ok(false); }, | Some(e) => e, From 59042ed096cc341342c31c9c53c164b76ec7ca2f Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Wed, 11 Jun 2025 01:42:19 +0100 Subject: [PATCH 22/28] log which room struggled to get mainline depth --- src/core/matrix/state_res/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/matrix/state_res/mod.rs b/src/core/matrix/state_res/mod.rs index e721e14c..ba9c013d 100644 --- a/src/core/matrix/state_res/mod.rs +++ b/src/core/matrix/state_res/mod.rs @@ -733,8 +733,12 @@ where Fut: Future> + Send, E: Event + Send + Sync, { + let mut room_id = None; while let Some(sort_ev) = event { debug!(event_id = sort_ev.event_id().as_str(), "mainline"); + if room_id.is_none() { + room_id = Some(sort_ev.room_id().to_owned()); + } let id = sort_ev.event_id(); if let Some(depth) = mainline_map.get(id) { @@ -753,7 +757,7 @@ where } } } - warn!("could not find a power event in the mainline map, defaulting to zero depth"); + warn!("could not find a power event in the mainline map for {room_id:?}, defaulting to zero depth"); Ok(0) } From 13bd3edbca34c1fcf5282fce947696ee22f8bb95 Mon Sep 17 00:00:00 2001 From: Jacob Taylor Date: Sat, 21 Jun 2025 08:02:49 -0700 Subject: [PATCH 23/28] change rocksdb stats level to 3 scale rocksdb background jobs and subcompactions change rocksdb default error level to info from error delete unused num_threads function fix warns from cargo --- conduwuit-example.toml | 2 +- src/core/config/mod.rs | 6 +++--- src/database/engine/db_opts.rs | 22 ++++------------------ 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/conduwuit-example.toml b/conduwuit-example.toml index 2c3721d0..a04819df 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -1041,7 +1041,7 @@ # 3 to 5 = Statistics with possible performance impact. # 6 = All statistics. # -#rocksdb_stats_level = 1 +#rocksdb_stats_level = 3 # This is a password that can be configured that will let you login to the # server bot account (currently `@conduit`) for emergency troubleshooting diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index 17da6492..f6f2cfc3 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -1207,7 +1207,7 @@ pub struct Config { /// 3 to 5 = Statistics with possible performance impact. /// 6 = All statistics. /// - /// default: 1 + /// default: 3 #[serde(default = "default_rocksdb_stats_level")] pub rocksdb_stats_level: u8, @@ -2265,7 +2265,7 @@ fn default_typing_client_timeout_max_s() -> u64 { 45 } fn default_rocksdb_recovery_mode() -> u8 { 1 } -fn default_rocksdb_log_level() -> String { "error".to_owned() } +fn default_rocksdb_log_level() -> String { "info".to_owned() } fn default_rocksdb_log_time_to_roll() -> usize { 0 } @@ -2297,7 +2297,7 @@ fn default_rocksdb_compression_level() -> i32 { 32767 } #[allow(clippy::doc_markdown)] fn default_rocksdb_bottommost_compression_level() -> i32 { 32767 } -fn default_rocksdb_stats_level() -> u8 { 1 } +fn default_rocksdb_stats_level() -> u8 { 3 } // I know, it's a great name #[must_use] diff --git a/src/database/engine/db_opts.rs b/src/database/engine/db_opts.rs index 18cec742..1299443d 100644 --- a/src/database/engine/db_opts.rs +++ b/src/database/engine/db_opts.rs @@ -1,8 +1,6 @@ -use std::{cmp, convert::TryFrom}; - -use conduwuit::{Config, Result, utils}; +use conduwuit::{Config, Result}; use rocksdb::{Cache, DBRecoveryMode, Env, LogLevel, Options, statistics::StatsLevel}; - +use conduwuit::config::{parallelism_scaled_i32, parallelism_scaled_u32}; use super::{cf_opts::cache_size_f64, logger::handle as handle_log}; /// Create database-wide options suitable for opening the database. This also @@ -23,8 +21,8 @@ pub(crate) fn db_options(config: &Config, env: &Env, row_cache: &Cache) -> Resul set_logging_defaults(&mut opts, config); // Processing - opts.set_max_background_jobs(num_threads::(config)?); - opts.set_max_subcompactions(num_threads::(config)?); + opts.set_max_background_jobs(parallelism_scaled_i32(1)); + opts.set_max_subcompactions(parallelism_scaled_u32(1)); opts.set_avoid_unnecessary_blocking_io(true); opts.set_max_file_opening_threads(0); @@ -126,15 +124,3 @@ fn set_logging_defaults(opts: &mut Options, config: &Config) { opts.set_callback_logger(rocksdb_log_level, &handle_log); } } - -fn num_threads>(config: &Config) -> Result { - const MIN_PARALLELISM: usize = 2; - - let requested = if config.rocksdb_parallelism_threads != 0 { - config.rocksdb_parallelism_threads - } else { - utils::available_parallelism() - }; - - utils::math::try_into::(cmp::max(MIN_PARALLELISM, requested)) -} From cb275b910bb5798f6de796e6761bd3b205a7c5c8 Mon Sep 17 00:00:00 2001 From: Jacob Taylor Date: Wed, 18 Jun 2025 12:48:27 -0700 Subject: [PATCH 24/28] make fetching key room events less smart --- src/core/matrix/state_res/event_auth.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/core/matrix/state_res/event_auth.rs b/src/core/matrix/state_res/event_auth.rs index fc1119de..ec70d684 100644 --- a/src/core/matrix/state_res/event_auth.rs +++ b/src/core/matrix/state_res/event_auth.rs @@ -242,12 +242,16 @@ where } */ - let (room_create_event, power_levels_event, sender_member_event) = join3( - fetch_state(&StateEventType::RoomCreate, ""), - fetch_state(&StateEventType::RoomPowerLevels, ""), - fetch_state(&StateEventType::RoomMember, sender.as_str()), - ) - .await; + // let (room_create_event, power_levels_event, sender_member_event) = join3( + // fetch_state(&StateEventType::RoomCreate, ""), + // fetch_state(&StateEventType::RoomPowerLevels, ""), + // fetch_state(&StateEventType::RoomMember, sender.as_str()), + // ) + // .await; + + let room_create_event = fetch_state(&StateEventType::RoomCreate, "").await; + let power_levels_event = fetch_state(&StateEventType::RoomPowerLevels, "").await; + let sender_member_event = fetch_state(&StateEventType::RoomMember, sender.as_str()).await; let room_create_event = match room_create_event { | None => { From 5484eff931cb0168013e06974c0bbf53f0d1d61f Mon Sep 17 00:00:00 2001 From: Jacob Taylor Date: Thu, 3 Jul 2025 14:39:10 -0700 Subject: [PATCH 25/28] lock the getter instead ??? c/o M --- src/service/rooms/event_handler/upgrade_outlier_pdu.rs | 2 +- src/service/rooms/state/mod.rs | 1 + src/service/rooms/timeline/create.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/service/rooms/event_handler/upgrade_outlier_pdu.rs b/src/service/rooms/event_handler/upgrade_outlier_pdu.rs index 4093cb05..05f88849 100644 --- a/src/service/rooms/event_handler/upgrade_outlier_pdu.rs +++ b/src/service/rooms/event_handler/upgrade_outlier_pdu.rs @@ -149,7 +149,7 @@ where let extremities: Vec<_> = self .services .state - .get_forward_extremities(room_id) + .get_forward_extremities(room_id, &state_lock) .map(ToOwned::to_owned) .ready_filter(|event_id| { // Remove any that are referenced by this incoming event's prev_events diff --git a/src/service/rooms/state/mod.rs b/src/service/rooms/state/mod.rs index 641aa6a9..92881126 100644 --- a/src/service/rooms/state/mod.rs +++ b/src/service/rooms/state/mod.rs @@ -388,6 +388,7 @@ impl Service { pub fn get_forward_extremities<'a>( &'a self, room_id: &'a RoomId, + _state_lock: &'a RoomMutexGuard, ) -> impl Stream + Send + '_ { let prefix = (room_id, Interfix); diff --git a/src/service/rooms/timeline/create.rs b/src/service/rooms/timeline/create.rs index 20ccaf56..1be2f58b 100644 --- a/src/service/rooms/timeline/create.rs +++ b/src/service/rooms/timeline/create.rs @@ -42,7 +42,7 @@ pub async fn create_hash_and_sign_event( let prev_events: Vec = self .services .state - .get_forward_extremities(room_id) + .get_forward_extremities(room_id, _mutex_lock) .take(20) .map(Into::into) .collect() From 2cd966db33a97af465e3bef55de6c7ea10d68890 Mon Sep 17 00:00:00 2001 From: Jacob Taylor Date: Thu, 3 Jul 2025 14:44:27 -0700 Subject: [PATCH 26/28] vehicle loan documentation now available at window 7 --- src/service/rooms/event_handler/upgrade_outlier_pdu.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/service/rooms/event_handler/upgrade_outlier_pdu.rs b/src/service/rooms/event_handler/upgrade_outlier_pdu.rs index 05f88849..bc2408df 100644 --- a/src/service/rooms/event_handler/upgrade_outlier_pdu.rs +++ b/src/service/rooms/event_handler/upgrade_outlier_pdu.rs @@ -6,6 +6,7 @@ use conduwuit::{ trace, utils::stream::{BroadbandExt, ReadyExt}, warn, + info }; use futures::{FutureExt, StreamExt, future::ready}; use ruma::{CanonicalJsonValue, RoomId, ServerName, events::StateEventType}; @@ -167,6 +168,8 @@ where .collect() .await; + if extremities.len() == 0 { info!("Retained zero extremities when upgrading outlier PDU to timeline PDU with {} previous events, event id: {}", incoming_pdu.prev_events.len(), incoming_pdu.event_id) } + debug!( "Retained {} extremities checked against {} prev_events", extremities.len(), From ac3f2e9e9d1844e50bee974c689945242365abdb Mon Sep 17 00:00:00 2001 From: Jacob Taylor Date: Sat, 21 Jun 2025 08:13:30 -0700 Subject: [PATCH 27/28] sender_workers scaling. this time, with feeling! --- src/core/config/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index f6f2cfc3..ad0dce52 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -1889,9 +1889,9 @@ pub struct Config { pub stream_amplification: usize, /// Number of sender task workers; determines sender parallelism. Default is - /// '4'. Override by setting a different value. Values clamped 1 to core count. + /// core count. Override by setting a different value. /// - /// default: 4 + /// default: core count #[serde(default = "default_sender_workers")] pub sender_workers: usize, @@ -2378,7 +2378,7 @@ fn default_stream_width_scale() -> f32 { 1.0 } fn default_stream_amplification() -> usize { 1024 } -fn default_sender_workers() -> usize { 4 } +fn default_sender_workers() -> usize { parallelism_scaled(1) } fn default_client_receive_timeout() -> u64 { 75 } From e76753b113483c3c3fda899af2f0def485437313 Mon Sep 17 00:00:00 2001 From: Jacob Taylor Date: Mon, 30 Jun 2025 15:25:11 -0700 Subject: [PATCH 28/28] more funny settings (part 3 of 12) --- src/core/config/mod.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index ad0dce52..bd44f9ff 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -2125,40 +2125,41 @@ fn default_db_write_buffer_capacity_mb() -> f64 { 48.0 + parallelism_scaled_f64( fn default_db_cache_capacity_mb() -> f64 { 512.0 + parallelism_scaled_f64(512.0) } -fn default_pdu_cache_capacity() -> u32 { parallelism_scaled_u32(50_000).saturating_add(500_000) } +fn default_pdu_cache_capacity() -> u32 { parallelism_scaled_u32(50_000).saturating_add(100_000) } fn default_cache_capacity_modifier() -> f64 { 1.0 } fn default_auth_chain_cache_capacity() -> u32 { - parallelism_scaled_u32(50_000).saturating_add(500_000) + parallelism_scaled_u32(50_000).saturating_add(100_000) } fn default_shorteventid_cache_capacity() -> u32 { - parallelism_scaled_u32(100_000).saturating_add(500_000) + parallelism_scaled_u32(100_000).saturating_add(100_000) } fn default_eventidshort_cache_capacity() -> u32 { - parallelism_scaled_u32(100_000).saturating_add(500_000) + parallelism_scaled_u32(50_000).saturating_add(100_000) } fn default_eventid_pdu_cache_capacity() -> u32 { - parallelism_scaled_u32(50_000).saturating_add(500_000) + parallelism_scaled_u32(50_000).saturating_add(100_000) } fn default_shortstatekey_cache_capacity() -> u32 { - parallelism_scaled_u32(50_000).saturating_add(500_000) + parallelism_scaled_u32(50_000).saturating_add(100_000) } fn default_statekeyshort_cache_capacity() -> u32 { - parallelism_scaled_u32(50_000).saturating_add(500_000) + parallelism_scaled_u32(50_000).saturating_add(100_000) } fn default_servernameevent_data_cache_capacity() -> u32 { - parallelism_scaled_u32(200_000).saturating_add(500_000) + parallelism_scaled_u32(100_000).saturating_add(100_000) } fn default_stateinfo_cache_capacity() -> u32 { - parallelism_scaled_u32(500).clamp(100, 12000) } + parallelism_scaled_u32(500).clamp(100, 12000) +} fn default_roomid_spacehierarchy_cache_capacity() -> u32 { parallelism_scaled_u32(500).clamp(100, 12000) }