Compare commits

...

30 Commits

Author SHA1 Message Date
Jade Ellis fdb40c9758 ci: Compare against the merge base to avoid unneded triggers 2026-04-12 17:36:22 +01:00
Jade Ellis d961fb1515 ci: Rename changelog check workflow 2026-04-12 16:33:38 +00:00
Jade Ellis 44ebf1f526 ci: Fail when changelog is missing 2026-04-12 16:33:38 +00:00
Jade Ellis f02a08de57 ci: Use labels for changelog rather than comments 2026-04-12 16:33:38 +00:00
Jade Ellis ab241f8f65 chore(deps): Update sd-notify 2026-04-12 16:36:28 +01:00
Renovate Bot 45d316999a chore(deps): update rust crate sd-notify to 0.5.0 2026-04-12 15:33:08 +00:00
Jade Ellis b81aea642a fix(deps): Upgrade crypto crates to matching versions 2026-04-12 16:29:02 +01:00
Renovate Bot f3e8160d9b chore(deps): update rust crate hmac to 0.13.0 2026-04-12 15:03:55 +00:00
Renovate Bot b80b9a7950 chore(deps): update rust crate ctor to 0.9.0 2026-04-12 14:27:21 +00:00
Jade Ellis c51acb7acb ci: Use upstream regsync installer action 2026-04-12 15:16:28 +01:00
timedout 5110930add fix: Allow server admins and v12 room creators to publish rooms 2026-04-12 14:09:53 +00:00
Henry-Hiles 7250561aed chore: clean up NixOS docs 2026-04-12 13:58:34 +00:00
Renovate Bot d7434f7047 chore(deps): lock file maintenance 2026-04-12 05:07:02 +00:00
Renovate Bot d5d0127ff4 chore(deps): update node-patch-updates to v2.0.9 2026-04-11 05:03:31 +00:00
Renovate Bot ab1fc060a7 chore(deps): lock file maintenance 2026-04-10 10:56:43 +00:00
Renovate Bot ddc9e795d8 chore(deps): update rust crate serde-saphyr to 0.0.23 2026-04-10 08:30:04 +00:00
Renovate Bot 87892a9739 chore(deps): update https://github.com/actions/github-script action to v9 2026-04-10 08:21:00 +00:00
Renovate Bot 3e2d454989 chore(deps): update dependency cargo-bins/cargo-binstall to v1.17.9 2026-04-10 08:20:12 +00:00
Henry-Hiles a79e7a01a8 fix: indentation in nixos file 2026-04-10 08:11:44 +00:00
Henry-Hiles b378cb8c5d fix: multiple top-level headers in generic file 2026-04-10 08:11:44 +00:00
Henry-Hiles 68e31282ef chore: remove nix hardened profile docs as the hardened profile will was removed from nixpkgs.
See https://github.com/NixOS/nixpkgs/pull/501199
2026-04-10 08:11:44 +00:00
Henry-Hiles f40e0c7773 feat: more specific docs on how to use the flake nix package 2026-04-10 08:11:44 +00:00
Henry-Hiles fbb855a404 feat: update build docs 2026-04-10 08:11:44 +00:00
Jade Ellis 2325e8fa4c chore: Update generated docs 2026-04-09 17:24:45 +01:00
Jade Ellis 6906d63013 docs: Changelog 2026-04-09 17:24:44 +01:00
Jade Ellis 16de2a2cc0 feat: Add ability to inspect build information and features at runtime
Also re-adds ability to inspect used features
2026-04-09 17:24:44 +01:00
Jade Ellis 108a4fe336 ci: Remove caching of /target directory
This directory seemed to grow exponentially, with incremental
compilation reaching 11GB+ and dependencies not finishing
2026-04-09 17:17:03 +01:00
Renovate Bot 83396db5de chore(deps): update https://github.com/samueldr/lix-gha-installer-action digest to f5e9419 2026-04-09 05:02:05 +00:00
timedout 839138c02e chore: Add news frag 2026-04-08 20:49:59 +00:00
timedout e03c90c2ac fix: Sign restricted joins when we're the authorising server 2026-04-08 20:49:59 +00:00
49 changed files with 991 additions and 1172 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ runs:
- name: Install sccache
uses: https://git.tomfos.tr/tom/sccache-action@v1
- name: Configure sccache
uses: https://github.com/actions/github-script@v8
uses: https://github.com/actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
core.exportVariable('ACTIONS_RESULTS_URL', process.env.ACTIONS_RESULTS_URL || '');
-31
View File
@@ -149,37 +149,6 @@ runs:
- name: Setup sccache
uses: https://git.tomfos.tr/tom/sccache-action@v1
- name: Cache dependencies
id: deps-cache
uses: actions/cache@v4
with:
path: |
target/**/.fingerprint
target/**/deps
target/**/*.d
target/**/.cargo-lock
target/**/CACHEDIR.TAG
target/**/.rustc_info.json
/timelord/
# Dependencies cache - based on Cargo.lock, survives source code changes
key: >-
continuwuity-deps-${{ steps.runner-os.outputs.slug }}-${{ steps.runner-os.outputs.arch }}-${{ steps.rust-setup.outputs.version }}${{ inputs.cache-key-suffix && format('-{0}', inputs.cache-key-suffix) || '' }}-${{ hashFiles('rust-toolchain.toml', '**/Cargo.lock') }}
restore-keys: |
continuwuity-deps-${{ steps.runner-os.outputs.slug }}-${{ steps.runner-os.outputs.arch }}-${{ steps.rust-setup.outputs.version }}${{ inputs.cache-key-suffix && format('-{0}', inputs.cache-key-suffix) || '' }}-
- name: Cache incremental compilation
id: incremental-cache
uses: actions/cache@v4
with:
path: |
target/**/incremental
# Incremental cache - based on source code changes
key: >-
continuwuity-incremental-${{ steps.runner-os.outputs.slug }}-${{ steps.runner-os.outputs.arch }}-${{ steps.rust-setup.outputs.version }}${{ inputs.cache-key-suffix && format('-{0}', inputs.cache-key-suffix) || '' }}-${{ hashFiles('rust-toolchain.toml', '**/Cargo.lock') }}-${{ hashFiles('**/*.rs', '**/Cargo.toml') }}
restore-keys: |
continuwuity-incremental-${{ steps.runner-os.outputs.slug }}-${{ steps.runner-os.outputs.arch }}-${{ steps.rust-setup.outputs.version }}${{ inputs.cache-key-suffix && format('-{0}', inputs.cache-key-suffix) || '' }}-${{ hashFiles('rust-toolchain.toml', '**/Cargo.lock') }}-
continuwuity-incremental-${{ steps.runner-os.outputs.slug }}-${{ steps.runner-os.outputs.arch }}-${{ steps.rust-setup.outputs.version }}${{ inputs.cache-key-suffix && format('-{0}', inputs.cache-key-suffix) || '' }}-
- name: End build cache restore group
shell: bash
run: echo "::endgroup::"
+33 -37
View File
@@ -1,8 +1,8 @@
name: Check Changelog
name: Checks / Changelog
on:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review]
types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled]
concurrency:
@@ -16,7 +16,7 @@ permissions:
jobs:
check-changelog:
name: Check for changelog
name: Check changelog is added
runs-on: ubuntu-latest
steps:
- name: Checkout repository
@@ -33,9 +33,9 @@ jobs:
git fetch origin ${GITHUB_BASE_REF}
# Check for Added (A) or Modified (M) files in changelog.d
CHANGELOG_CHANGES=$(git diff --name-status origin/${GITHUB_BASE_REF} HEAD -- changelog.d/)
CHANGELOG_CHANGES=$(git diff --name-status origin/${GITHUB_BASE_REF}...HEAD -- changelog.d/)
SRC_CHANGES=$(git diff --name-status origin/${GITHUB_BASE_REF} HEAD -- src/)
SRC_CHANGES=$(git diff --name-status origin/${GITHUB_BASE_REF}...HEAD -- src/)
echo "Changes in changelog.d/:"
echo "$CHANGELOG_CHANGES"
@@ -54,8 +54,8 @@ jobs:
echo "src_changed=false" >> $GITHUB_OUTPUT
fi
- name: Manage PR Comment
uses: https://github.com/actions/github-script@v8
- name: Manage PR Labels
uses: https://github.com/actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
HAS_CHANGELOG: ${{ steps.check_files.outputs.has_changelog }}
SRC_CHANGED: ${{ steps.check_files.outputs.src_changed }}
@@ -63,41 +63,37 @@ jobs:
script: |
const hasChangelog = process.env.HAS_CHANGELOG === 'true';
const srcChanged = process.env.SRC_CHANGED === 'true';
const commentSignature = '<!-- changelog-check-action -->';
const commentBody = `${commentSignature}\nPlease add a changelog fragment to \`changelog.d/\` describing your changes.`;
const { data: currentUser } = await github.rest.users.getAuthenticated();
const { data: comments } = await github.rest.issues.listComments({
const { data: pullRequest } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
pull_number: context.issue.number,
});
const botComment = comments.find(comment =>
comment.user.id === currentUser.id &&
comment.body.includes(commentSignature)
);
const currentLabels = pullRequest.labels.map(l => l.name);
const shouldWarn = srcChanged && !hasChangelog;
if (!shouldWarn) {
if (botComment) {
console.log('Changelog found or not required. Deleting existing warning comment.');
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
});
}
if (hasChangelog) {
console.log('PR has changelog');
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['Changelog/Added'],
});
} else if (currentLabels.includes('Changelog/None')) {
console.log('PR has Changelog/None label, skipping.');
} else if (srcChanged) {
console.log('PR is missing changelog');
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['Changelog/Missing'],
});
core.setFailed("Missing changelog entry (detected)");
} else if (currentLabels.includes('Changelog/Missing')) {
core.setFailed("Missing changelog entry (label)");
} else {
if (!botComment) {
console.log('Changelog missing and required. Creating warning comment.');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: commentBody,
});
}
console.log('Changelog not needed');
// Changelog is probably not needed
}
+2 -4
View File
@@ -51,10 +51,8 @@ jobs:
# owner: continuwuity
# repositories: continuwuity
- name: Install regctl
uses: https://forgejo.ellis.link/continuwuation/regclient-actions/regctl-installer@main
with:
binary: regsync
- name: Install regsync
uses: https://github.com/regclient/actions/regsync-installer@main
- name: Check what images need mirroring
run: |
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
token: ${{ secrets.FORGEJO_TOKEN }}
- name: Install Lix
uses: https://github.com/samueldr/lix-gha-installer-action@7b7f14d320d6aacfb65bd1ef761566b3b69e474c
uses: https://github.com/samueldr/lix-gha-installer-action@f5e94192f565f53d84f41a056956dc0d3183b343
with:
extra_nix_config: experimental-features = nix-command flakes flake-self-attrs
Generated
+328 -358
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -39,7 +39,7 @@ features = ["ffi", "std", "union"]
version = "0.7.0"
[workspace.dependencies.ctor]
version = "0.6.0"
version = "0.9.0"
[workspace.dependencies.cargo_toml]
version = "0.22"
@@ -60,7 +60,7 @@ default-features = false
# used for TURN server authentication
[workspace.dependencies.hmac]
version = "0.12.1"
version = "0.13.0"
default-features = false
# used for checking if an IP is in specific subnets / CIDR ranges easier
@@ -159,7 +159,7 @@ features = ["raw_value"]
# Used for appservice registration files
[workspace.dependencies.serde-saphyr]
version = "0.0.21"
version = "0.0.23"
# Used to load forbidden room/user regex from config
[workspace.dependencies.serde_regex]
@@ -404,7 +404,7 @@ version = "0.10.8"
default-features = false
[workspace.dependencies.sha1]
version = "0.10.6"
version = "0.11.0"
default-features = false
# optional opentelemetry, performance measurements, flamegraphs, etc for performance measurements and monitoring
@@ -481,7 +481,7 @@ default-features = false
features = ["resource"]
[workspace.dependencies.sd-notify]
version = "0.4.5"
version = "0.5.0"
default-features = false
[workspace.dependencies.hardened_malloc-rs]
+1
View File
@@ -0,0 +1 @@
Added admin commands to get build information and features. Contributed by @Jade
+1
View File
@@ -0,0 +1 @@
Fixed restricted joins not being signed when we are being used as an authorising server. Contributed by @nex, reported by [vel](matrix:u/vel:nhjkl.com?action=chat).
+2 -2
View File
@@ -527,13 +527,13 @@
# which users must agree to when registering an account.
#
# Example:
# ```
# ```ignore
# [global.registration_terms.privacy_policy]
# en = { name = "Privacy Policy", url = "https://homeserver.example/en/privacy_policy.html" }
# es = { name = "Política de Privacidad", url = "https://homeserver.example/es/privacy_policy.html" }
# ```
#
#registration_terms = false
#registration_terms = {}
# Controls whether encrypted rooms and events are allowed.
#
+1 -1
View File
@@ -48,7 +48,7 @@ EOF
# Developer tool versions
# renovate: datasource=github-releases depName=cargo-bins/cargo-binstall
ENV BINSTALL_VERSION=1.17.8
ENV BINSTALL_VERSION=1.17.9
# renovate: datasource=github-releases depName=psastras/sbom-rs
ENV CARGO_SBOM_VERSION=0.9.1
# renovate: datasource=crate depName=lddtree
+1 -1
View File
@@ -18,7 +18,7 @@ RUN --mount=type=cache,target=/etc/apk/cache apk add \
# Developer tool versions
# renovate: datasource=github-releases depName=cargo-bins/cargo-binstall
ENV BINSTALL_VERSION=1.17.8
ENV BINSTALL_VERSION=1.17.9
# renovate: datasource=github-releases depName=psastras/sbom-rs
ENV CARGO_SBOM_VERSION=0.9.1
# renovate: datasource=crate depName=lddtree
+34 -26
View File
@@ -14,6 +14,7 @@ Download the binary for your architecture (x86_64 or aarch64) -
run the `uname -m` to check which you need.
Prebuilt binaries are available from:
- **Tagged releases**: [Latest release page](https://forgejo.ellis.link/continuwuation/continuwuity/releases/latest)
- **Development builds**: CI artifacts from the `main` branch
(includes Debian/Ubuntu packages)
@@ -42,32 +43,36 @@ build profile with
[link-time optimisation (LTO)](https://doc.rust-lang.org/cargo/reference/profiles.html#lto)
and, for amd64, target the haswell CPU architecture.
### Nix
Theres a Nix package defined in our flake, available for Linux and MacOS. Add continuwuity as an input to your flake, and use `inputs.continuwuity.packages.${system}.default` to get a working Continuwuity package.
If you simply wish to generate a binary using Nix, you can run `nix build git+https://forgejo.ellis.link/continuwuation/continuwuity` to generate a binary in `result/bin/conduwuit`.
### Compiling
Alternatively, you may compile the binary yourself.
### Building with the Rust toolchain
#### Using Docker
If wanting to build using standard Rust toolchains, make sure you install:
If you would like to build using docker, you can run the command `docker build -f ./docker/Dockerfile -t forgejo.ellis.link/continuwuation/continuwuity:main .` to compile continuwuity.
- (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
#### Manual
##### Dependencies
- Run `nix develop` to get a devshell with everything you need
- Or, install the following:
- (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
##### Build
You can build Continuwuity using `cargo build --release`.
Continuwuity supports various optional features that can be enabled during compilation. Please see the Cargo.toml file for a comprehensive list, or ask in our rooms.
### 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
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.
## Adding a Continuwuity user
While Continuwuity can run as any user, it is better to use dedicated users for
@@ -128,13 +133,11 @@ and entering the following:
ReadWritePaths=/path/to/custom/database/path
```
### Example systemd Unit File
<details>
<summary>Click to expand systemd unit file (conduwuit.service)</summary>
```ini file="../../pkg/conduwuit.service"
```
@@ -202,23 +205,27 @@ sudo systemctl enable --now caddy
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 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`
`/server_version`
You can optionally reverse proxy the following individual routes:
- `/.well-known/matrix/client` and `/.well-known/matrix/server` if using
Continuwuity to perform delegation (see the `[global.well_known]` config section)
Continuwuity to perform delegation (see the `[global.well_known]` config section)
- `/.well-known/matrix/support` if using Continuwuity to send the homeserver admin
contact and support page (formerly known as MSC1929)
contact and support page (formerly known as MSC1929)
- `/` if you would like to see `hewwo from conduwuit woof!` at the root
See the following spec pages for more details on these files:
- [`/.well-known/matrix/server`](https://spec.matrix.org/latest/client-server-api/#getwell-knownmatrixserver)
- [`/.well-known/matrix/client`](https://spec.matrix.org/latest/client-server-api/#getwell-knownmatrixclient)
- [`/.well-known/matrix/support`](https://spec.matrix.org/latest/client-server-api/#getwell-knownmatrixsupport)
Examples of delegation:
- https://continuwuity.org/.well-known/matrix/server
- https://continuwuity.org/.well-known/matrix/client
- https://ellis.link/.well-known/matrix/server
@@ -232,6 +239,7 @@ header, making federation non-functional. If you find a workaround, please share
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 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;`
@@ -271,17 +279,17 @@ curl https://your.server.name:8448/_matrix/federation/v1/version
```
- To check if your server can communicate with other homeservers, use the
[Matrix Federation Tester](https://federationtester.mtrnord.blog/). If you can
register but cannot join federated rooms, check your configuration and verify
that port 8448 is open and forwarded correctly.
[Matrix Federation Tester](https://federationtester.mtrnord.blog/). If you can
register but cannot join federated rooms, check your configuration and verify
that port 8448 is open and forwarded correctly.
# What's next?
## What's next?
## Audio/Video calls
### Audio/Video calls
For Audio/Video call functionality see the [Calls](../calls.md) page.
## Appservices
### Appservices
If you want to set up an appservice, take a look at the [Appservice
Guide](../appservices.md).
+37 -93
View File
@@ -1,40 +1,40 @@
# Continuwuity for NixOS
NixOS packages Continuwuity as `matrix-continuwuity`. This package includes both the Continuwuity software and a dedicated NixOS module for configuration and deployment.
## Nix package
## Installation methods
You can get a Nix package for Continuwuity from the following sources:
You can acquire Continuwuity with Nix (or [Lix][lix]) from these sources:
- Directly from Nixpkgs: `pkgs.matrix-continuwuity`
- Or, using `continuwuity.packages.${system}.default` from:
- The `flake.nix` at the root of the Continuwuity repo, by adding Continuwuity to your flake inputs:
* 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
```nix
inputs.continuwuity.url = "git+https://forgejo.ellis.link/continuwuation/continuwuity";
```
- The `default.nix` at the root of the Continuwuity repo
## NixOS module
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.
Continuwuity has an official NixOS module that simplifies configuration and deployment. The module is available in Nixpkgs as `services.matrix-continuwuity`.
Here's a basic example of how to use the module:
```nix
{ config, pkgs, ... }:
services.matrix-continuwuity = {
enable = true;
settings = {
global = {
server_name = "example.com";
{
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" ];
};
# Continuwuity listens on localhost by default,
# address and port are handled automatically
# You can add any further configuration here, e.g.
# trusted_servers = [ "matrix.org" ];
};
};
}
};
```
### Available options
@@ -45,86 +45,30 @@ The NixOS module provides these configuration options:
- `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)
- `package`: The Continuwuity package to use, defaults to `pkgs.matrix-continuwuity`
- You may want to override this to be from our flake, for faster updates and unstable versions:
```nix
package = inputs.continuwuity.packages.${pkgs.stdenv.hostPlatform.system}.default;
```
- `admin.enable`: Whether to add the `conduwuit` binary to `PATH` for administration (enabled by default)
- `settings`: The Continuwuity configuration
Use the `settings` option to configure Continuwuity itself. See the [example configuration file](../reference/config.mdx) for all available options.
### UNIX sockets
The NixOS module natively supports UNIX sockets through the `global.unix_socket_path` option. When using UNIX sockets, set `global.address` to `null`:
Settings are automatically translated from Nix to TOML. For example, the following line of Nix:
```nix
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
# ...
};
};
};
settings.global.well_known.client = "https://matrix.example.com";
```
The module automatically sets the correct `RestrictAddressFamilies` in the systemd service configuration to allow access to UNIX sockets.
Would become this equivalent TOML configuration:
### RocksDB database
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. Either disable/hide `scudo` from Continuwuity or disable jemalloc like this:
```nix
services.matrix-continuwuity = {
enable = true;
package = pkgs.matrix-continuwuity.override {
enableJemalloc = false;
};
# ...
};
```toml
[global.well_known]
client = "https://matrix.example.com"
```
## 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/
[hardened.nix]: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/hardened.nix
You'll need to set up a reverse proxy (like NGINX or Caddy) to expose Continuwuity to the internet. You can configure your reverse proxy using NixOS options (e.g. `services.caddy`).
See the [reverse proxy setup guide](./generic.mdx#setting-up-the-reverse-proxy) for information on correct reverse proxy configuration.
+12
View File
@@ -133,6 +133,18 @@ pusher service
Returns all the pushers for the user
### `!admin query pusher delete-pusher`
Deletes a specific pusher by ID
### `!admin query pusher delete-all-user`
Deletes all pushers for a user
### `!admin query pusher delete-all-device`
Deletes all pushers associated with a device ID
## `!admin query short`
short service
+8
View File
@@ -47,3 +47,11 @@ Restart the server
## `!admin server shutdown`
Shutdown the server
## `!admin server list-features`
List features built into the server
## `!admin server build-info`
Build information
+4
View File
@@ -157,3 +157,7 @@ Force joins all local users to the specified room.
At least 1 server admin must be in the room to reduce abuse.
Requires the `--yes-i-want-to-do-this` flag.
## `!admin users reset-push-rules`
Resets the push-rules (notification settings) of the target user to the server defaults
Generated
+24 -24
View File
@@ -3,11 +3,11 @@
"advisory-db": {
"flake": false,
"locked": {
"lastModified": 1773786698,
"narHash": "sha256-o/J7ZculgwSs1L4H4UFlFZENOXTJzq1X0n71x6oNNvY=",
"lastModified": 1775907537,
"narHash": "sha256-vbeLNgmsx1Z6TwnlDV0dKyeBCcon3UpkV9yLr/yc6HM=",
"owner": "rustsec",
"repo": "advisory-db",
"rev": "99e9de91bb8b61f06ef234ff84e11f758ecd5384",
"rev": "d99f7b9eb81731bddebf80a355f8be7b2f8b1b28",
"type": "github"
},
"original": {
@@ -18,11 +18,11 @@
},
"crane": {
"locked": {
"lastModified": 1773189535,
"narHash": "sha256-E1G/Or6MWeP+L6mpQ0iTFLpzSzlpGrITfU2220Gq47g=",
"lastModified": 1775839657,
"narHash": "sha256-SPm9ck7jh3Un9nwPuMGbRU04UroFmOHjLP56T10MOeM=",
"owner": "ipetkov",
"repo": "crane",
"rev": "6fa2fb4cf4a89ba49fc9dd5a3eb6cde99d388269",
"rev": "7cf72d978629469c4bd4206b95c402514c1f6000",
"type": "github"
},
"original": {
@@ -39,11 +39,11 @@
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1773732206,
"narHash": "sha256-HKibxaUXyWd4Hs+ZUnwo6XslvaFqFqJh66uL9tphU4Q=",
"lastModified": 1775891769,
"narHash": "sha256-EOfVlTKw2n8w1uhfh46GS4hEGnQ7oWrIWQfIY6utIkI=",
"owner": "nix-community",
"repo": "fenix",
"rev": "0aa13c1b54063a8d8679b28a5cd357ba98f4a56b",
"rev": "6fbc54dde15aee725bdc7aae5e478849685d5f56",
"type": "github"
},
"original": {
@@ -74,11 +74,11 @@
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1772408722,
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
"lastModified": 1775087534,
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
"type": "github"
},
"original": {
@@ -89,11 +89,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1773734432,
"narHash": "sha256-IF5ppUWh6gHGHYDbtVUyhwy/i7D261P7fWD1bPefOsw=",
"lastModified": 1775710090,
"narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "cda48547b432e8d3b18b4180ba07473762ec8558",
"rev": "4c1018dae018162ec878d42fec712642d214fdfa",
"type": "github"
},
"original": {
@@ -105,11 +105,11 @@
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1772328832,
"narHash": "sha256-e+/T/pmEkLP6BHhYjx6GmwP5ivonQQn0bJdH9YrRB+Q=",
"lastModified": 1774748309,
"narHash": "sha256-+U7gF3qxzwD5TZuANzZPeJTZRHS29OFQgkQ2kiTJBIQ=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "c185c7a5e5dd8f9add5b2f8ebeff00888b070742",
"rev": "333c4e0545a6da976206c74db8773a1645b5870a",
"type": "github"
},
"original": {
@@ -132,11 +132,11 @@
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1773697963,
"narHash": "sha256-xdKI77It9PM6eNrCcDZsnP4SKulZwk8VkDgBRVMnCb8=",
"lastModified": 1775843361,
"narHash": "sha256-j53ZgyDvmYf3Sjh1IPvvTjqa614qUfVQSzj59+MpzkY=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "2993637174252ff60a582fd1f55b9ab52c39db6d",
"rev": "9eb97ea96d8400e8957ddd56702e962614296583",
"type": "github"
},
"original": {
@@ -153,11 +153,11 @@
]
},
"locked": {
"lastModified": 1773297127,
"narHash": "sha256-6E/yhXP7Oy/NbXtf1ktzmU8SdVqJQ09HC/48ebEGBpk=",
"lastModified": 1775636079,
"narHash": "sha256-pc20NRoMdiar8oPQceQT47UUZMBTiMdUuWrYu2obUP0=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "71b125cd05fbfd78cab3e070b73544abe24c5016",
"rev": "790751ff7fd3801feeaf96d7dc416a8d581265ba",
"type": "github"
},
"original": {
+123 -553
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -2,6 +2,7 @@
name = "conduwuit_admin"
description.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
@@ -79,6 +80,7 @@ conduwuit-database.workspace = true
conduwuit-macros.workspace = true
conduwuit-service.workspace = true
const-str.workspace = true
ctor.workspace = true
futures.workspace = true
lettre.workspace = true
log.workspace = true
+2
View File
@@ -3,6 +3,8 @@
#![allow(clippy::enum_glob_use)]
#![allow(clippy::too_many_arguments)]
conduwuit_macros::introspect_crate! {}
pub(crate) mod admin;
pub(crate) mod context;
pub(crate) mod processor;
+95 -1
View File
@@ -1,4 +1,4 @@
use std::{path::PathBuf, sync::Arc};
use std::{fmt::Write, path::PathBuf, sync::Arc};
use conduwuit::{
Err, Result,
@@ -153,3 +153,97 @@ pub(super) async fn shutdown(&self) -> Result {
self.write_str("Shutting down server...").await
}
#[admin_command]
pub(super) async fn list_features(&self) -> Result {
let mut enabled_features = conduwuit::info::introspection::ENABLED_FEATURES
.lock()
.expect("locked")
.iter()
.flat_map(|(_, f)| f.iter())
.collect::<Vec<_>>();
enabled_features.sort_unstable();
enabled_features.dedup();
let mut available_features = conduwuit::build_metadata::WORKSPACE_FEATURES
.iter()
.flat_map(|(_, f)| f.iter())
.collect::<Vec<_>>();
available_features.sort_unstable();
available_features.dedup();
let mut features = String::new();
for feature in available_features {
let active = enabled_features.contains(&feature);
let emoji = if active { "" } else { "" };
let remark = if active { "[enabled]" } else { "" };
writeln!(features, "{emoji} {feature} {remark}")?;
}
self.write_str(&features).await
}
#[admin_command]
pub(super) async fn build_info(&self) -> Result {
use conduwuit::build_metadata::built;
let mut info = String::new();
// Version information
writeln!(info, "# Build Information\n")?;
writeln!(info, "**Version:** {}", built::PKG_VERSION)?;
writeln!(info, "**Package:** {}", built::PKG_NAME)?;
writeln!(info, "**Description:** {}", built::PKG_DESCRIPTION)?;
// Git information
writeln!(info, "\n## Git Information\n")?;
if let Some(hash) = conduwuit::build_metadata::GIT_COMMIT_HASH {
writeln!(info, "**Commit Hash:** {hash}")?;
}
if let Some(hash) = conduwuit::build_metadata::GIT_COMMIT_HASH_SHORT {
writeln!(info, "**Commit Hash (short):** {hash}")?;
}
if let Some(url) = conduwuit::build_metadata::GIT_REMOTE_WEB_URL {
writeln!(info, "**Repository:** {url}")?;
}
if let Some(url) = conduwuit::build_metadata::GIT_REMOTE_COMMIT_URL {
writeln!(info, "**Commit URL:** {url}")?;
}
// Build environment
writeln!(info, "\n## Build Environment\n")?;
writeln!(info, "**Profile:** {}", built::PROFILE)?;
writeln!(info, "**Optimization Level:** {}", built::OPT_LEVEL)?;
writeln!(info, "**Debug:** {}", built::DEBUG)?;
writeln!(info, "**Target:** {}", built::TARGET)?;
writeln!(info, "**Host:** {}", built::HOST)?;
// Rust compiler information
writeln!(info, "\n## Compiler Information\n")?;
writeln!(info, "**Rustc Version:** {}", built::RUSTC_VERSION)?;
if !built::RUSTDOC_VERSION.is_empty() {
writeln!(info, "**Rustdoc Version:** {}", built::RUSTDOC_VERSION)?;
}
// Target configuration
writeln!(info, "\n## Target Configuration\n")?;
writeln!(info, "**Architecture:** {}", built::CFG_TARGET_ARCH)?;
writeln!(info, "**OS:** {}", built::CFG_OS)?;
writeln!(info, "**Family:** {}", built::CFG_FAMILY)?;
writeln!(info, "**Endianness:** {}", built::CFG_ENDIAN)?;
writeln!(info, "**Pointer Width:** {} bits", built::CFG_POINTER_WIDTH)?;
if !built::CFG_ENV.is_empty() {
writeln!(info, "**Environment:** {}", built::CFG_ENV)?;
}
// CI information
if let Some(ci) = built::CI_PLATFORM {
writeln!(info, "\n## CI Platform\n")?;
writeln!(info, "**Platform:** {ci}")?;
}
self.write_str(&info).await
}
+6
View File
@@ -52,4 +52,10 @@ pub enum ServerCommand {
/// Shutdown the server
Shutdown,
/// List features built into the server
ListFeatures,
/// Build information
BuildInfo,
}
+3
View File
@@ -2,6 +2,7 @@
name = "conduwuit_api"
description.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
@@ -76,8 +77,10 @@ axum.workspace = true
base64.workspace = true
bytes.workspace = true
conduwuit-core.workspace = true
conduwuit-macros.workspace = true
conduwuit-service.workspace = true
const-str.workspace = true
ctor.workspace = true
futures.workspace = true
hmac.workspace = true
http.workspace = true
+56 -25
View File
@@ -1,7 +1,9 @@
use std::iter::once;
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduwuit::{
Err, Event, Result, err, info,
Err, Event, Result, RoomVersion, err, info,
utils::{
TryFutureExtExt,
math::Expected,
@@ -30,12 +32,14 @@ use ruma::{
events::{
StateEventType,
room::{
create::RoomCreateEventContent,
join_rules::{JoinRule, RoomJoinRulesEventContent},
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
},
},
uint,
};
use tokio::join;
use crate::Ruma;
@@ -339,36 +343,63 @@ pub(crate) async fn get_public_rooms_filtered_helper(
})
}
/// Check whether the user can publish to the room directory via power levels of
/// room history visibility event or room creator
/// Checks whether the given user ID is allowed to publish the target room to
/// the server's public room directory. Users are allowed to publish rooms if
/// they are server admins, room creators (in v12), or have the power level to
/// send `m.room.canonical_alias`.
async fn user_can_publish_room(
services: &Services,
user_id: &UserId,
room_id: &RoomId,
) -> Result<bool> {
match services
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")
.await
if services.users.is_admin(user_id).await {
// Server admins can always publish to their own room directory.
return Ok(true);
}
let (create_event, room_version, power_levels_content) = join!(
services
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomCreate, ""),
services.rooms.state.get_room_version(room_id),
services
.rooms
.state_accessor
.room_state_get_content::<RoomPowerLevelsEventContent>(
room_id,
&StateEventType::RoomPowerLevels,
""
)
);
let room_version = room_version
.as_ref()
.map_err(|_| err!(Request(NotFound("Unknown room"))))?;
let create_event = create_event.map_err(|_| err!(Request(NotFound("Unknown room"))))?;
if RoomVersion::new(room_version)
.expect("room version must be supported")
.explicitly_privilege_room_creators
{
| Ok(event) => serde_json::from_str(event.content().get())
.map_err(|_| err!(Database("Invalid event content for m.room.power_levels")))
.map(|content: RoomPowerLevelsEventContent| {
RoomPowerLevels::from(content)
.user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)
}),
| _ => {
match services
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomCreate, "")
.await
{
| Ok(event) => Ok(event.sender() == user_id),
| _ => Err!(Request(Forbidden("User is not allowed to publish this room"))),
}
},
let create_content: RoomCreateEventContent =
serde_json::from_str(create_event.content().get())
.map_err(|_| err!(Database("Invalid event content for m.room.create")))?;
let is_creator = create_content
.additional_creators
.unwrap_or_default()
.into_iter()
.chain(once(create_event.sender().to_owned()))
.any(|sender| sender == user_id);
if is_creator {
return Ok(true);
}
}
match power_levels_content.map(RoomPowerLevels::from) {
| Ok(pl) => Ok(pl.user_can_send_state(user_id, StateEventType::RoomCanonicalAlias)),
| Err(e) =>
if e.is_not_found() {
Ok(create_event.sender() == user_id)
} else {
Err!(Database("Invalid event content for m.room.power_levels: {e}"))
},
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ use std::time::{Duration, SystemTime};
use axum::extract::State;
use base64::{Engine as _, engine::general_purpose};
use conduwuit::{Err, Result, utils};
use hmac::{Hmac, Mac};
use hmac::{Hmac, KeyInit, Mac};
use ruma::{SecondsSinceUnixEpoch, UserId, api::client::voip::get_turn_server_info};
use sha1::Sha1;
+3
View File
@@ -3,6 +3,9 @@
extern crate conduwuit_core as conduwuit;
extern crate conduwuit_service as service;
conduwuit_macros::introspect_crate! {}
pub mod client;
pub mod router;
pub mod server;
+8 -1
View File
@@ -71,7 +71,7 @@ async fn create_join_event(
let room_version_id = services.rooms.state.get_room_version(room_id).await?;
trace!("Generating event ID and converting to canonical json");
let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, &room_version_id) else {
let Ok((event_id, mut value)) = gen_event_id_canonical_json(pdu, &room_version_id) else {
// Event could not be converted to canonical json
return Err!(Request(BadJson("Could not convert event to canonical json.")));
};
@@ -187,6 +187,13 @@ async fn create_join_event(
"Joining user did not pass restricted room's rules."
)));
}
services
.server_keys
.hash_and_sign_event(&mut value, &room_version_id)
.map_err(|e| {
err!(Request(InvalidParam(warn!("Failed to sign send_join event: {e}"))))
})?;
}
let mutex_lock = services
+2 -1
View File
@@ -2,6 +2,7 @@
name = "conduwuit_build_metadata"
description.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
@@ -27,6 +28,6 @@ crate-type = [
[build-dependencies]
built = { version = "0.8", features = [] }
cargo_metadata = { version = "0.23.1" }
[lints]
workspace = true
+81 -2
View File
@@ -1,5 +1,9 @@
use std::process::Command;
use std::{
collections::BTreeMap, env, fmt::Write as FmtWrite, fs, io::Write, path::Path,
process::Command,
};
use cargo_metadata::MetadataCommand;
fn run_git_command(args: &[&str]) -> Option<String> {
Command::new("git")
.args(args)
@@ -11,12 +15,60 @@ fn run_git_command(args: &[&str]) -> Option<String> {
.filter(|s| !s.is_empty())
}
fn get_env(env_var: &str) -> Option<String> {
match std::env::var(env_var) {
match env::var(env_var) {
| Ok(val) if !val.is_empty() => Some(val),
| _ => None,
}
}
fn main() {
println!("cargo:rerun-if-changed=Cargo.toml");
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); // Cargo.toml path
let manifest_path = Path::new(&manifest_dir).join("Cargo.toml");
let metadata = MetadataCommand::new()
.manifest_path(&manifest_path)
.no_deps()
.exec()
.expect("failed to parse `cargo metadata`");
let workspace_packages = metadata
.workspace_members
.iter()
.map(|package| {
let package = metadata.packages.iter().find(|p| p.id == *package).unwrap();
println!("cargo:rerun-if-changed={}", package.manifest_path.as_str());
package
})
.collect::<Vec<_>>();
// Extract available features from workspace packages
let mut available_features: BTreeMap<String, Vec<String>> = BTreeMap::new();
for package in &workspace_packages {
let crate_name = package
.name
.trim_start_matches("conduwuit-")
.replace('-', "_");
let features: Vec<String> = package.features.keys().cloned().collect();
if !features.is_empty() {
available_features.insert(crate_name, features);
}
}
// Generate Rust code for available features
let features_code = generate_features_code(&available_features);
let features_dst =
Path::new(&env::var("OUT_DIR").expect("OUT_DIR not set")).join("available_features.rs");
let mut features_file = fs::File::create(features_dst).unwrap();
features_file.write_all(features_code.as_bytes()).unwrap();
let dst = Path::new(&env::var("OUT_DIR").expect("OUT_DIR not set")).join("pkg.json");
let mut out_file = fs::File::create(dst).unwrap();
out_file
.write_all(format!("{workspace_packages:?}").as_bytes())
.unwrap();
// built gets the default crate from the workspace. Not sure if this is intended
// behavior, but it's what we want.
built::write_built_file().expect("Failed to acquire build-time information");
@@ -91,3 +143,30 @@ fn main() {
println!("cargo:rerun-if-env-changed=GIT_REMOTE_URL");
println!("cargo:rerun-if-env-changed=GIT_REMOTE_COMMIT_URL");
}
fn generate_features_code(features: &BTreeMap<String, Vec<String>>) -> String {
let mut code = String::from(
r#"
/// All available features for workspace crates
pub const WORKSPACE_FEATURES: &[(&str, &[&str])] = &[
"#,
);
for (crate_name, feature_list) in features {
write!(code, " (\"{crate_name}\", &[").unwrap();
for (i, feature) in feature_list.iter().enumerate() {
if i > 0 {
code.push_str(", ");
}
write!(code, "\"{feature}\"").unwrap();
}
code.push_str("]),\n");
}
code.push_str(
r#"];
"#,
);
code
}
+4
View File
@@ -2,6 +2,10 @@ pub mod built {
include!(concat!(env!("OUT_DIR"), "/built.rs"));
}
// Include generated available features
// This provides: pub const WORKSPACE_FEATURES: &[(&str, &[&str])]
include!(concat!(env!("OUT_DIR"), "/available_features.rs"));
pub static GIT_COMMIT_HASH: Option<&str> = option_env!("GIT_COMMIT_HASH");
pub static GIT_COMMIT_HASH_SHORT: Option<&str> = option_env!("GIT_COMMIT_HASH_SHORT");
+1
View File
@@ -2,6 +2,7 @@
name = "conduwuit_core"
description.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
+7
View File
@@ -0,0 +1,7 @@
//! Information about features the crates were compiled with.
//! Only available for crates that have called the `introspect_crate` macro
use std::collections::BTreeMap;
pub static ENABLED_FEATURES: std::sync::Mutex<BTreeMap<&str, &[&str]>> =
std::sync::Mutex::new(BTreeMap::new());
+1
View File
@@ -1,3 +1,4 @@
pub mod introspection;
pub mod room_version;
pub mod version;
+3
View File
@@ -19,6 +19,7 @@ pub use ::smallstr;
pub use ::smallvec;
pub use ::toml;
pub use ::tracing;
pub use conduwuit_build_metadata as build_metadata;
pub use config::Config;
pub use error::Error;
pub use info::{
@@ -34,6 +35,8 @@ pub use utils::{implement, result, result::Result};
pub use crate as conduwuit_core;
conduwuit_macros::introspect_crate! {}
#[cfg(any(not(conduwuit_mods), not(feature = "conduwuit_mods")))]
pub mod mods {
#[macro_export]
+3
View File
@@ -2,6 +2,7 @@
name = "conduwuit_database"
description.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
@@ -54,7 +55,9 @@ bindgen-runtime = [
[dependencies]
async-channel.workspace = true
conduwuit-core.workspace = true
conduwuit-macros.workspace = true
const-str.workspace = true
ctor.workspace = true
futures.workspace = true
log.workspace = true
minicbor.workspace = true
+2
View File
@@ -3,6 +3,8 @@
extern crate conduwuit_core as conduwuit;
extern crate rust_rocksdb as rocksdb;
conduwuit_macros::introspect_crate! {}
conduwuit::mod_ctor! {}
conduwuit::mod_dtor! {}
+2
View File
@@ -2,6 +2,7 @@
name = "conduwuit_macros"
description.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
@@ -17,6 +18,7 @@ syn.workspace = true
quote.workspace = true
proc-macro2.workspace = true
itertools.workspace = true
cargo_toml.workspace = true
[lints]
workspace = true
+63
View File
@@ -0,0 +1,63 @@
use proc_macro2::TokenStream;
use quote::quote;
use crate::Result;
pub(super) fn introspect(_args: TokenStream) -> Result<TokenStream> {
let cargo_crate_name = std::env::var("CARGO_CRATE_NAME").unwrap();
let crate_name = cargo_crate_name.trim_start_matches("conduwuit_");
let is_core = cargo_crate_name == "conduwuit_core";
let flags = std::env::args().collect::<Vec<_>>();
let mut enabled_features = Vec::new();
append_features(&mut enabled_features, flags);
let enabled_count = enabled_features.len();
let import_path = if is_core {
quote! { use crate::conduwuit_core; }
} else {
quote! { use ::conduwuit_core; }
};
let ret = quote! {
#[doc(hidden)]
mod __compile_introspection {
#import_path
/// Features that were enabled when this crate was compiled
const ENABLED: [&str; #enabled_count] = [#( #enabled_features ),*];
const CRATE_NAME: &str = #crate_name;
/// Register this crate's features with the global registry during static initialization
#[::ctor::ctor]
fn register() {
conduwuit_core::info::introspection::ENABLED_FEATURES.lock().unwrap().insert(#crate_name, &ENABLED);
}
#[::ctor::dtor]
fn unregister() {
conduwuit_core::info::introspection::ENABLED_FEATURES.lock().unwrap().remove(#crate_name);
}
}
};
Ok(ret)
}
fn append_features(features: &mut Vec<String>, flags: Vec<String>) {
let mut next_is_cfg = false;
for flag in flags {
let is_cfg = flag == "--cfg";
let is_feature = flag.starts_with("feature=");
if std::mem::replace(&mut next_is_cfg, is_cfg) && is_feature {
if let Some(feature) = flag
.split_once('=')
.map(|(_, feature)| feature.trim_matches('"'))
{
features.push(feature.to_owned());
}
}
}
}
+8
View File
@@ -1,4 +1,5 @@
mod admin;
mod build_info;
mod config;
mod debug;
mod implement;
@@ -44,6 +45,13 @@ pub fn config_example_generator(args: TokenStream, input: TokenStream) -> TokenS
attribute_macro::<ItemStruct, _>(args, input, config::example_generator)
}
#[proc_macro]
pub fn introspect_crate(input: TokenStream) -> TokenStream {
build_info::introspect(input.into())
.unwrap_or_else(|e| e.to_compile_error())
.into()
}
fn attribute_macro<I, F>(args: TokenStream, input: TokenStream, func: F) -> TokenStream
where
F: Fn(I, &[Meta]) -> Result<TokenStream>,
+2
View File
@@ -207,8 +207,10 @@ conduwuit-database.workspace = true
conduwuit-router.workspace = true
conduwuit-service.workspace = true
conduwuit-build-metadata.workspace = true
conduwuit-macros.workspace = true
clap.workspace = true
ctor.workspace = true
console-subscriber.optional = true
console-subscriber.workspace = true
const-str.workspace = true
+2
View File
@@ -4,6 +4,8 @@ use std::sync::{Arc, atomic::Ordering};
use conduwuit_core::{debug_info, error};
conduwuit_macros::introspect_crate! {}
mod clap;
mod deadlock;
mod logging;
+3
View File
@@ -2,6 +2,7 @@
name = "conduwuit_router"
description.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
@@ -99,9 +100,11 @@ bytes.workspace = true
conduwuit-admin.workspace = true
conduwuit-api.workspace = true
conduwuit-core.workspace = true
conduwuit-macros.workspace = true
conduwuit-service.workspace = true
conduwuit-web.workspace = true
const-str.workspace = true
ctor.workspace = true
futures.workspace = true
http.workspace = true
http-body-util.workspace = true
+2
View File
@@ -8,6 +8,8 @@ mod serve;
extern crate conduwuit_core as conduwuit;
conduwuit_macros::introspect_crate! {}
use std::{panic::AssertUnwindSafe, pin::Pin, sync::Arc};
use conduwuit::{Error, Result, Server};
+2 -2
View File
@@ -73,7 +73,7 @@ pub(crate) async fn start(server: Arc<Server>) -> Result<Arc<Services>> {
let services = Services::build(server).await?.start().await?;
#[cfg(all(feature = "systemd", target_os = "linux"))]
sd_notify::notify(false, &[sd_notify::NotifyState::Ready])
sd_notify::notify(&[sd_notify::NotifyState::Ready])
.expect("failed to notify systemd of ready state");
debug!("Started");
@@ -86,7 +86,7 @@ pub(crate) async fn stop(services: Arc<Services>) -> Result<()> {
debug!("Shutting down...");
#[cfg(all(feature = "systemd", target_os = "linux"))]
sd_notify::notify(false, &[sd_notify::NotifyState::Stopping])
sd_notify::notify(&[sd_notify::NotifyState::Stopping])
.expect("failed to notify systemd of stopping state");
// Wait for all completions before dropping or we'll lose them to the module
+3
View File
@@ -2,6 +2,7 @@
name = "conduwuit_service"
description.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
@@ -88,7 +89,9 @@ base64.workspace = true
bytes.workspace = true
conduwuit-core.workspace = true
conduwuit-database.workspace = true
conduwuit-macros.workspace = true
const-str.workspace = true
ctor.workspace = true
either.workspace = true
futures.workspace = true
governor.workspace = true
+2 -2
View File
@@ -70,7 +70,7 @@ impl Deref for Service {
fn handle_reload(&self) -> Result {
if self.server.config.config_reload_signal {
#[cfg(all(feature = "systemd", target_os = "linux"))]
sd_notify::notify(false, &[
sd_notify::notify(&[
sd_notify::NotifyState::Reloading,
sd_notify::NotifyState::monotonic_usec_now().expect("Failed to read monotonic time"),
])
@@ -80,7 +80,7 @@ fn handle_reload(&self) -> Result {
self.reload(&config_paths)?;
#[cfg(all(feature = "systemd", target_os = "linux"))]
sd_notify::notify(false, &[sd_notify::NotifyState::Ready])
sd_notify::notify(&[sd_notify::NotifyState::Ready])
.expect("failed to notify systemd of ready state");
}
+3
View File
@@ -3,6 +3,9 @@
extern crate conduwuit_core as conduwuit;
extern crate conduwuit_database as database;
conduwuit_macros::introspect_crate! {}
mod manager;
mod migrations;
mod service;
+1
View File
@@ -2,6 +2,7 @@
name = "conduwuit_web"
description.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true