mirror of
https://forgejo.ellis.link/continuwuation/continuwuity/
synced 2026-04-01 20:26:18 +00:00
Compare commits
168 Commits
jade/sende
...
jade/http3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40536b13da | ||
|
|
cacd8681d1 | ||
|
|
b095518e6f | ||
|
|
a91add4aca | ||
|
|
7fec48423a | ||
|
|
2f6b7c7a40 | ||
|
|
48ab6adec1 | ||
|
|
592244d5aa | ||
|
|
091893f8bc | ||
|
|
6eba6a838e | ||
|
|
1a11c784f5 | ||
|
|
55ccfdb973 | ||
|
|
a9a39e6d5e | ||
|
|
38bf1ccbcc | ||
|
|
b7a8cbdb42 | ||
|
|
4e1dac32a5 | ||
|
|
7b21c3fd9f | ||
|
|
f566ca1b93 | ||
|
|
debe411e23 | ||
|
|
dc0d6a9220 | ||
|
|
2efdb6fb0d | ||
|
|
576348a445 | ||
|
|
f322b6dca0 | ||
|
|
a1ed77a99c | ||
|
|
01b5dffeee | ||
|
|
ea3c00da43 | ||
|
|
047eba0442 | ||
|
|
11a088be5d | ||
|
|
dc6bd4e541 | ||
|
|
2bf9207cc4 | ||
|
|
b2a87e2fb9 | ||
|
|
7d0686f33c | ||
|
|
082c44f355 | ||
|
|
117c581948 | ||
|
|
cb846a3ad1 | ||
|
|
81b984b2cc | ||
|
|
e2961390ee | ||
|
|
cb75e836e0 | ||
|
|
cb7a988b1b | ||
|
|
aa5400bcef | ||
|
|
ff4dddd673 | ||
|
|
c22b17fb29 | ||
|
|
3da7fa24db | ||
|
|
d15ac1d3c1 | ||
|
|
a9ebdf58e2 | ||
|
|
f1ab27d344 | ||
|
|
8bc6e6ccca | ||
|
|
60a3abe752 | ||
|
|
e3b874d336 | ||
|
|
f3f82831b4 | ||
|
|
26aac1408e | ||
|
|
be8f62396a | ||
|
|
40996a6602 | ||
|
|
9cae531f90 | ||
|
|
56eea935b6 | ||
|
|
fcb646f8c4 | ||
|
|
57b21c1b32 | ||
|
|
8d66500c99 | ||
|
|
abacf1dc20 | ||
|
|
134e5cadaf | ||
|
|
8ec0f0d830 | ||
|
|
0453544036 | ||
|
|
89ad809270 | ||
|
|
ecd3a4eb41 | ||
|
|
5506997ca0 | ||
|
|
abc0683d59 | ||
|
|
dd60beb9fb | ||
|
|
d9520f9382 | ||
|
|
40bb5366bb | ||
|
|
f82bd77073 | ||
|
|
7d84ba5ff2 | ||
|
|
69a8937584 | ||
|
|
b2ec13d342 | ||
|
|
4e55e1ea90 | ||
|
|
f5f3108d5f | ||
|
|
d1e1ee6156 | ||
|
|
ae16a45515 | ||
|
|
077bda23a6 | ||
|
|
a2bf0c1223 | ||
|
|
b9b1ff87f2 | ||
|
|
3c0146d437 | ||
|
|
7485d4aa91 | ||
|
|
39bdb4c5a2 | ||
|
|
55fb3b8848 | ||
|
|
19146166c0 | ||
|
|
f47027006f | ||
|
|
b7a8f71e14 | ||
|
|
c7378d15ab | ||
|
|
7beeab270e | ||
|
|
6a812b7776 | ||
|
|
b1f4bbe89e | ||
|
|
6701f88bf9 | ||
|
|
62b9e8227b | ||
|
|
7369b58d91 | ||
|
|
f6df44b13f | ||
|
|
f243b383cb | ||
|
|
e0b7d03018 | ||
|
|
184ae2ebb9 | ||
|
|
0ea0d09b97 | ||
|
|
6763952ce4 | ||
|
|
e2da8301df | ||
|
|
296a4b92d6 | ||
|
|
00c054d356 | ||
|
|
2558ec0c2a | ||
|
|
56bc3c184e | ||
|
|
5c1b90b463 | ||
|
|
0dbb774559 | ||
|
|
16e0566c84 | ||
|
|
489b6e4ecb | ||
|
|
e71f75a58c | ||
|
|
082ed5b70c | ||
|
|
76fe8c4cdc | ||
|
|
c4a9f7a6d1 | ||
|
|
a047199fb4 | ||
|
|
411c9da743 | ||
|
|
fb54f2058c | ||
|
|
358273226c | ||
|
|
fd9bbb08ed | ||
|
|
53184cd2fc | ||
|
|
25f7d80a8c | ||
|
|
02fa0ba0b8 | ||
|
|
572b228f40 | ||
|
|
b0a61e38da | ||
|
|
401dff20eb | ||
|
|
f2a50e8f62 | ||
|
|
36e80b0af4 | ||
|
|
c9a4c546e2 | ||
|
|
da8b60b4ce | ||
|
|
89afaa94ac | ||
|
|
2b5563cee3 | ||
|
|
6cb9d50383 | ||
|
|
77c0f6e0c6 | ||
|
|
c85e710760 | ||
|
|
59346fc766 | ||
|
|
9c5e735888 | ||
|
|
fe74e82318 | ||
|
|
cb79a3b9d7 | ||
|
|
ebc8df1c4d | ||
|
|
b667a963cf | ||
|
|
5a6b909b37 | ||
|
|
dba9cf0ad2 | ||
|
|
287ddd9bc5 | ||
|
|
79a278b9e8 | ||
|
|
6c5d658ef2 | ||
|
|
70c43abca8 | ||
|
|
6a9b47c52e | ||
|
|
c042de96f8 | ||
|
|
7a6acd1c82 | ||
|
|
d260c4fcc2 | ||
|
|
fa15de9764 | ||
|
|
e6c7a4ae60 | ||
|
|
5bed4ad81d | ||
|
|
587abe9d14 | ||
|
|
c499042a76 | ||
|
|
86e450a835 | ||
|
|
4c796029bb | ||
|
|
fc3615c46b | ||
|
|
7375f7a68e | ||
|
|
ae28fe92d2 | ||
|
|
00eeeb78de | ||
|
|
a028049e6f | ||
|
|
7b159bc8c8 | ||
|
|
66fcedf08b | ||
|
|
3f790844f3 | ||
|
|
89be9d1efc | ||
|
|
60dd6baffd | ||
|
|
99a10998b4 | ||
|
|
05c6b5df75 |
@@ -1,9 +1,9 @@
|
||||
# Local build and dev artifacts
|
||||
target/
|
||||
!target/debug/conduwuit
|
||||
|
||||
# Docker files
|
||||
Dockerfile*
|
||||
docker/
|
||||
|
||||
# IDE files
|
||||
.vscode
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
container: ["ubuntu-latest", "ubuntu-previous", "debian-latest", "debian-oldstable"]
|
||||
container: [ "ubuntu-latest", "ubuntu-previous", "debian-latest", "debian-oldstable" ]
|
||||
container:
|
||||
image: "ghcr.io/tcpipuk/act-runner:${{ matrix.container }}"
|
||||
|
||||
@@ -30,6 +30,28 @@ jobs:
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "distribution=$DISTRIBUTION" >> $GITHUB_OUTPUT
|
||||
echo "Debian distribution: $DISTRIBUTION ($VERSION)"
|
||||
- name: Work around llvm-project#153385
|
||||
id: llvm-workaround
|
||||
run: |
|
||||
if [ -f /usr/share/apt/default-sequoia.config ]; then
|
||||
echo "Applying workaround for llvm-project#153385"
|
||||
mkdir -p /etc/crypto-policies/back-ends/
|
||||
cp /usr/share/apt/default-sequoia.config /etc/crypto-policies/back-ends/apt-sequoia.config
|
||||
sed -i 's/\(sha1\.second_preimage_resistance = \)2026-02-01/\12026-06-01/' /etc/crypto-policies/back-ends/apt-sequoia.config
|
||||
else
|
||||
echo "No workaround needed for llvm-project#153385"
|
||||
fi
|
||||
- name: Pick compatible clang version
|
||||
id: clang-version
|
||||
run: |
|
||||
# both latest need to use clang-23, but oldstable and previous can just use clang
|
||||
if [[ "${{ matrix.container }}" == "ubuntu-latest" || "${{ matrix.container }}" == "debian-latest" ]]; then
|
||||
echo "Using clang-23 package for ${{ matrix.container }}"
|
||||
echo "version=clang-23" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Using default clang package for ${{ matrix.container }}"
|
||||
echo "version=clang" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Checkout repository with full history
|
||||
uses: actions/checkout@v6
|
||||
@@ -105,7 +127,7 @@ jobs:
|
||||
run: |
|
||||
apt-get update -y
|
||||
# Build dependencies for rocksdb
|
||||
apt-get install -y clang liburing-dev
|
||||
apt-get install -y liburing-dev ${{ steps.clang-version.outputs.version }}
|
||||
|
||||
- name: Run cargo-deb
|
||||
id: cargo-deb
|
||||
|
||||
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -1,4 +1,4 @@
|
||||
github: [JadedBlueEyes, nexy7574]
|
||||
github: [JadedBlueEyes, nexy7574, gingershaped]
|
||||
custom:
|
||||
- https://ko-fi.com/nexy7574
|
||||
- https://ko-fi.com/JadedBlueEyes
|
||||
|
||||
@@ -23,7 +23,7 @@ repos:
|
||||
- id: check-added-large-files
|
||||
|
||||
- repo: https://github.com/crate-ci/typos
|
||||
rev: v1.41.0
|
||||
rev: v1.43.4
|
||||
hooks:
|
||||
- id: typos
|
||||
- id: typos
|
||||
@@ -31,7 +31,7 @@ repos:
|
||||
stages: [commit-msg]
|
||||
|
||||
- repo: https://github.com/crate-ci/committed
|
||||
rev: v1.1.9
|
||||
rev: v1.1.10
|
||||
hooks:
|
||||
- id: committed
|
||||
|
||||
|
||||
@@ -6,14 +6,13 @@ extend-exclude = ["*.csr", "*.lock", "pnpm-lock.yaml"]
|
||||
extend-ignore-re = [
|
||||
"(?Rm)^.*(#|//|<!--)\\s*spellchecker:disable-line(\\s*-->)$", # Ignore a line by making it trail with a `spellchecker:disable-line` comment
|
||||
"^[0-9a-f]{7,}$", # Commit hashes
|
||||
|
||||
"4BA7",
|
||||
# some heuristics for base64 strings
|
||||
"[A-Za-z0-9+=]{72,}",
|
||||
"([A-Za-z0-9+=]|\\\\\\s\\*){72,}",
|
||||
"[0-9+][A-Za-z0-9+]{30,}[a-z0-9+]",
|
||||
"\\$[A-Z0-9+][A-Za-z0-9+]{6,}[a-z0-9+]",
|
||||
"\\b[a-z0-9+/=][A-Za-z0-9+/=]{7,}[a-z0-9+/=][A-Z]\\b",
|
||||
|
||||
# In the renovate config
|
||||
".ontainer"
|
||||
]
|
||||
@@ -25,3 +24,5 @@ extend-ignore-re = [
|
||||
"continuwity" = "continuwuity"
|
||||
"execuse" = "execuse"
|
||||
"oltp" = "OTLP"
|
||||
|
||||
rememvering = "remembering"
|
||||
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -7,6 +7,5 @@
|
||||
"continuwuity",
|
||||
"homeserver",
|
||||
"homeservers"
|
||||
],
|
||||
"rust-analyzer.cargo.features": ["full"]
|
||||
]
|
||||
}
|
||||
|
||||
145
CHANGELOG.md
145
CHANGELOG.md
@@ -1,37 +1,150 @@
|
||||
# Continuwuity v0.5.5 (2026-02-15)
|
||||
|
||||
## Features
|
||||
|
||||
- Added unstable support for [MSC4406:
|
||||
`M_SENDER_IGNORED`](https://github.com/matrix-org/matrix-spec-proposals/pull/4406).
|
||||
Contributed by @nex ([#1308](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1308))
|
||||
- Introduce a resolver command to allow flushing a server from the cache or to flush the complete cache. Contributed by
|
||||
@Omar007 ([#1349](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1349))
|
||||
- Improved the handling of restricted join rules and improved the performance of local-first joins. Contributed by
|
||||
@nex. ([#1368](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1368))
|
||||
- You can now set a custom User Agent for URL previews; the default one has been modified to be less likely to be
|
||||
rejected. Contributed by @trashpanda ([#1372](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1372))
|
||||
- Improved the first-time setup experience for new homeserver administrators:
|
||||
- Account registration is disabled on the first run, except for with a new special registration token that is logged
|
||||
to the console.
|
||||
- Other helpful information is logged to the console as well, including a giant warning if open registration is
|
||||
enabled.
|
||||
- The default index page now says to check the console for setup instructions if no accounts have been created.
|
||||
- Once the first admin account is created, an improved welcome message is sent to the admin room.
|
||||
|
||||
Contributed by @ginger.
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fixed invites sent to other users in the same homeserver not being properly sent down sync. Users with missing or
|
||||
broken invites should clear their client caches after updating to make them appear. ([#1249](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1249))
|
||||
- LDAP-enabled servers will no longer have all admins demoted when LDAP-controlled admins are not configured.
|
||||
Contributed by @Jade ([#1307](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1307))
|
||||
- Fixed sliding sync not resolving wildcard state key requests, enabling Video/Audio calls in Element X. ([#1370](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1370))
|
||||
|
||||
## Misc
|
||||
|
||||
- #1344
|
||||
|
||||
# Continuwuity v0.5.4 (2026-02-08)
|
||||
|
||||
## Features
|
||||
|
||||
- The announcement checker will now announce errors it encounters in the first run to the admin room, plus a few other
|
||||
misc improvements. Contributed by @Jade ([#1288](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1288))
|
||||
- Drastically improved the performance and reliability of account deactivations. Contributed by
|
||||
@nex ([#1314](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1314))
|
||||
- Refuse to process requests for and events in rooms that we no longer have any local users in (reduces state resets
|
||||
and improves performance). Contributed by
|
||||
@nex ([#1316](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1316))
|
||||
- Added server-specific admin API routes to ban and unban rooms, for use with moderation bots. Contributed by @nex
|
||||
([#1301](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1301))
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fix the generated configuration containing uncommented optional sections. Contributed by
|
||||
@Jade ([#1290](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1290))
|
||||
- Fixed specification non-compliance when handling remote media errors. Contributed by
|
||||
@nex ([#1298](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1298))
|
||||
- UIAA requests which check for out-of-band success (sent by matrix-js-sdk) will no longer create unhelpful errors in
|
||||
the logs. Contributed by @ginger ([#1305](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1305))
|
||||
- Use exists instead of contains to save writing to a buffer in `src/service/users/mod.rs`: `is_login_disabled`.
|
||||
Contributed
|
||||
by @aprilgrimoire. ([#1340](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1340))
|
||||
- Fixed backtraces being swallowed during panics. Contributed by
|
||||
@jade ([#1337](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1337))
|
||||
- Fixed a potential vulnerability that could allow an evil remote server to return malicious events during the room join
|
||||
and knock process. Contributed by @nex, reported by violet & [mat](https://matdoes.dev).
|
||||
- Fixed a race condition that could result in outlier PDUs being incorrectly marked as visible to a remote server.
|
||||
Contributed by @nex, reported by violet & [mat](https://matdoes.dev).
|
||||
- ACLs are no longer case-sensitive. Contributed by @nex, reported by [vel](matrix:u/vel:nhjkl.com?action=chat).
|
||||
|
||||
## Docs
|
||||
|
||||
- Fixed Fedora install instructions. Contributed by
|
||||
@julian45 ([#1342](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1342))
|
||||
|
||||
# Continuwuity 0.5.3 (2026-01-12)
|
||||
|
||||
## Features
|
||||
|
||||
- Improve the display of nested configuration with the `!admin server show-config` command. Contributed by
|
||||
@Jade ([#1279](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1279))
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fixed `M_BAD_JSON` error when sending invites to other servers or when providing joins. Contributed by
|
||||
@nex ([#1286](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1286))
|
||||
|
||||
## Docs
|
||||
|
||||
- Improve admin command documentation generation. Contributed by
|
||||
@ginger ([#1280](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1280))
|
||||
|
||||
## Misc
|
||||
|
||||
- Improve timeout-related code for federation and URL previews. Contributed by
|
||||
@Jade ([#1278](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1278))
|
||||
|
||||
# Continuwuity 0.5.2 (2026-01-09)
|
||||
|
||||
## Features
|
||||
|
||||
- Added support for issuing additional registration tokens, stored in the database, which supplement the existing registration token hardcoded in the config file. These tokens may optionally expire after a certain number of uses or after a certain amount of time has passed. Additionally, the `registration_token_file` configuration option is superseded by this feature and **has been removed**. Use the new `!admin token` command family to manage registration tokens. Contributed by @ginger (#783).
|
||||
- Implemented a configuration defined admin list independent of the admin room. Contributed by @Terryiscool160. (#1253)
|
||||
- Added support for invite and join anti-spam via Draupnir and Meowlnir, similar to that of synapse-http-antispam. Contributed by @nex. (#1263)
|
||||
- Implemented account locking functionality, to complement user suspension. Contributed by @nex. (#1266)
|
||||
- Added admin command to forcefully log out all of a user's existing sessions. Contributed by @nex. (#1271)
|
||||
- Implemented toggling the ability for an account to log in without mutating any of its data. Contributed by @nex. (#1272)
|
||||
- Add support for custom room create event timestamps, to allow generating custom prefixes in hashed room IDs. Contributed by @nex. (#1277)
|
||||
- Certain potentially dangerous admin commands are now restricted to only be usable in the admin room and server console. Contributed by @ginger.
|
||||
- Added support for issuing additional registration tokens, stored in the database, which supplement the existing
|
||||
registration token hardcoded in the config file. These tokens may optionally expire after a certain number of uses or
|
||||
after a certain amount of time has passed. Additionally, the `registration_token_file` configuration option is
|
||||
superseded by this feature and **has been removed**. Use the new `!admin token` command family to manage registration
|
||||
tokens. Contributed by @ginger (#783).
|
||||
- Implemented a configuration defined admin list independent of the admin room. Contributed by
|
||||
@Terryiscool160. ([#1253](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1253))
|
||||
- Added support for invite and join anti-spam via Draupnir and Meowlnir, similar to that of synapse-http-antispam.
|
||||
Contributed by @nex. ([#1263](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1263))
|
||||
- Implemented account locking functionality, to complement user suspension. Contributed by
|
||||
@nex. ([#1266](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1266))
|
||||
- Added admin command to forcefully log out all of a user's existing sessions. Contributed by
|
||||
@nex. ([#1271](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1271))
|
||||
- Implemented toggling the ability for an account to log in without mutating any of its data. Contributed by @nex. (
|
||||
[#1272](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1272))
|
||||
- Add support for custom room create event timestamps, to allow generating custom prefixes in hashed room IDs.
|
||||
Contributed by @nex. ([#1277](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1277))
|
||||
- Certain potentially dangerous admin commands are now restricted to only be usable in the admin room and server
|
||||
console. Contributed by @ginger.
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fixed unreliable room summary fetching and improved error messages. Contributed by @nex. (#1257)
|
||||
- Client requested timeout parameter is now applied to e2ee key lookups and claims. Related federation requests are now also concurrent. Contributed by @nex. (#1261)
|
||||
- Fixed the whoami endpoint returning HTTP 404 instead of HTTP 403, which confused some appservices. Contributed by @nex. (#1276)
|
||||
- Fixed unreliable room summary fetching and improved error messages. Contributed by
|
||||
@nex. ([#1257](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1257))
|
||||
- Client requested timeout parameter is now applied to e2ee key lookups and claims. Related federation requests are now
|
||||
also concurrent. Contributed by @nex. ([#1261](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1261))
|
||||
- Fixed the whoami endpoint returning HTTP 404 instead of HTTP 403, which confused some appservices. Contributed by
|
||||
@nex. ([#1276](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1276))
|
||||
|
||||
## Misc
|
||||
|
||||
- The `console` feature is now enabled by default, allowing the server console to be used for running admin commands directly. To automatically open the console on startup, set the `admin_console_automatic` config option to `true`. Contributed by @ginger.
|
||||
- The `console` feature is now enabled by default, allowing the server console to be used for running admin commands
|
||||
directly. To automatically open the console on startup, set the `admin_console_automatic` config option to `true`.
|
||||
Contributed by @ginger.
|
||||
- We now (finally) document our container image mirrors. Contributed by @Jade
|
||||
|
||||
|
||||
# Continuwuity 0.5.0 (2025-12-30)
|
||||
|
||||
**This release contains a CRITICAL vulnerability patch, and you must update as soon as possible**
|
||||
|
||||
## Features
|
||||
|
||||
- Enabled the OTLP exporter in default builds, and allow configuring the exporter protocol. (@Jade). (#1251)
|
||||
- Enabled the OTLP exporter in default builds, and allow configuring the exporter protocol. (
|
||||
@Jade). ([#1251](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1251))
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- Don't allow admin room upgrades, as this can break the admin room (@timedout) (#1245)
|
||||
- Fix invalid creators in power levels during upgrade to v12 (@timedout) (#1245)
|
||||
- Don't allow admin room upgrades, as this can break the admin room (
|
||||
@timedout) ([#1245](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1245))
|
||||
- Fix invalid creators in power levels during upgrade to v12 (
|
||||
@timedout) ([#1245](https://forgejo.ellis.link/continuwuation/continuwuity/pulls/1245))
|
||||
|
||||
1379
Cargo.lock
generated
1379
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
17
Cargo.toml
17
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["src/*", "xtask/*"]
|
||||
members = ["src/*", "xtask/"]
|
||||
default-members = ["src/*"]
|
||||
|
||||
[workspace.package]
|
||||
@@ -12,7 +12,7 @@ license = "Apache-2.0"
|
||||
# See also `rust-toolchain.toml`
|
||||
readme = "README.md"
|
||||
repository = "https://forgejo.ellis.link/continuwuation/continuwuity"
|
||||
version = "0.5.2"
|
||||
version = "0.5.5"
|
||||
|
||||
[workspace.metadata.crane]
|
||||
name = "conduwuit"
|
||||
@@ -158,7 +158,7 @@ features = ["raw_value"]
|
||||
|
||||
# Used for appservice registration files
|
||||
[workspace.dependencies.serde-saphyr]
|
||||
version = "0.0.10"
|
||||
version = "0.0.19"
|
||||
|
||||
# Used to load forbidden room/user regex from config
|
||||
[workspace.dependencies.serde_regex]
|
||||
@@ -342,7 +342,7 @@ version = "0.1.2"
|
||||
# Used for matrix spec type definitions and helpers
|
||||
[workspace.dependencies.ruma]
|
||||
git = "https://forgejo.ellis.link/continuwuation/ruwuma"
|
||||
rev = "f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
|
||||
rev = "b496b7f38d517149361a882e75d3fd4faf210441"
|
||||
features = [
|
||||
"compat",
|
||||
"rand",
|
||||
@@ -378,7 +378,8 @@ features = [
|
||||
"unstable-msc4210", # remove legacy mentions
|
||||
"unstable-extensible-events",
|
||||
"unstable-pdu",
|
||||
"unstable-msc4155"
|
||||
"unstable-msc4155",
|
||||
"unstable-msc4143", # livekit well_known response
|
||||
]
|
||||
|
||||
[workspace.dependencies.rust-rocksdb]
|
||||
@@ -548,6 +549,12 @@ features = ["sync", "tls-rustls", "rustls-provider"]
|
||||
[workspace.dependencies.resolv-conf]
|
||||
version = "0.7.5"
|
||||
|
||||
[workspace.dependencies.yansi]
|
||||
version = "1.0.1"
|
||||
|
||||
[workspace.dependencies.askama]
|
||||
version = "0.14.0"
|
||||
|
||||
#
|
||||
# Patches
|
||||
#
|
||||
|
||||
@@ -57,9 +57,10 @@ ### What are the project's goals?
|
||||
|
||||
### Can I try it out?
|
||||
|
||||
Check out the [documentation](https://continuwuity.org) for installation instructions.
|
||||
Check out the [documentation](https://continuwuity.org) for installation instructions, or join one of these vetted public homeservers running Continuwuity to get a feel for things!
|
||||
|
||||
There are currently no open registration Continuwuity instances available.
|
||||
- https://continuwuity.rocks -- A public demo server operated by the Continuwuity Team.
|
||||
- https://federated.nexus -- Federated Nexus is a community resource hosting multiple FOSS (especially federated) services, including Matrix and Forgejo.
|
||||
|
||||
### What are we working on?
|
||||
|
||||
|
||||
@@ -2,11 +2,7 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Path to Complement's source code
|
||||
#
|
||||
# The `COMPLEMENT_SRC` environment variable is set in the Nix dev shell, which
|
||||
# points to a store path containing the Complement source code. It's likely you
|
||||
# want to just pass that as the first argument to use it here.
|
||||
# The root path where complement is available.
|
||||
COMPLEMENT_SRC="${COMPLEMENT_SRC:-$1}"
|
||||
|
||||
# A `.jsonl` file to write test logs to
|
||||
@@ -15,7 +11,10 @@ LOG_FILE="${2:-complement_test_logs.jsonl}"
|
||||
# A `.jsonl` file to write test results to
|
||||
RESULTS_FILE="${3:-complement_test_results.jsonl}"
|
||||
|
||||
COMPLEMENT_BASE_IMAGE="${COMPLEMENT_BASE_IMAGE:-complement-conduwuit:main}"
|
||||
# The base docker image to use for complement tests
|
||||
# You can build the default with `docker build -t continuwuity:complement -f ./docker/complement.Dockerfile .`
|
||||
# after running `cargo build`. Only the debug binary is used.
|
||||
COMPLEMENT_BASE_IMAGE="${COMPLEMENT_BASE_IMAGE:-continuwuity:complement}"
|
||||
|
||||
# Complement tests that are skipped due to flakiness/reliability issues or we don't implement such features and won't for a long time
|
||||
SKIPPED_COMPLEMENT_TESTS='TestPartialStateJoin.*|TestRoomDeleteAlias/Parallel/Regular_users_can_add_and_delete_aliases_when_m.*|TestRoomDeleteAlias/Parallel/Can_delete_canonical_alias|TestUnbanViaInvite.*|TestRoomState/Parallel/GET_/publicRooms_lists.*"|TestRoomDeleteAlias/Parallel/Users_with_sufficient_power-level_can_delete_other.*'
|
||||
@@ -34,25 +33,6 @@ toplevel="$(git rev-parse --show-toplevel)"
|
||||
|
||||
pushd "$toplevel" > /dev/null
|
||||
|
||||
if [ ! -f "complement_oci_image.tar.gz" ]; then
|
||||
echo "building complement conduwuit image"
|
||||
|
||||
# if using macOS, use linux-complement
|
||||
#bin/nix-build-and-cache just .#linux-complement
|
||||
bin/nix-build-and-cache just .#complement
|
||||
#nix build -L .#complement
|
||||
|
||||
echo "complement conduwuit image tar.gz built at \"result\""
|
||||
|
||||
echo "loading into docker"
|
||||
docker load < result
|
||||
popd > /dev/null
|
||||
else
|
||||
echo "skipping building a complement conduwuit image as complement_oci_image.tar.gz was already found, loading this"
|
||||
|
||||
docker load < complement_oci_image.tar.gz
|
||||
popd > /dev/null
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "running go test with:"
|
||||
@@ -72,24 +52,16 @@ env \
|
||||
set -o pipefail
|
||||
|
||||
# Post-process the results into an easy-to-compare format, sorted by Test name for reproducible results
|
||||
cat "$LOG_FILE" | jq -s -c 'sort_by(.Test)[]' | jq -c '
|
||||
jq -s -c 'sort_by(.Test)[]' < "$LOG_FILE" | jq -c '
|
||||
select(
|
||||
(.Action == "pass" or .Action == "fail" or .Action == "skip")
|
||||
and .Test != null
|
||||
) | {Action: .Action, Test: .Test}
|
||||
' > "$RESULTS_FILE"
|
||||
|
||||
#if command -v gotestfmt &> /dev/null; then
|
||||
# echo "using gotestfmt on $LOG_FILE"
|
||||
# grep '{"Time":' "$LOG_FILE" | gotestfmt > "complement_test_logs_gotestfmt.log"
|
||||
#fi
|
||||
|
||||
echo ""
|
||||
echo ""
|
||||
echo "complement logs saved at $LOG_FILE"
|
||||
echo "complement results saved at $RESULTS_FILE"
|
||||
#if command -v gotestfmt &> /dev/null; then
|
||||
# echo "complement logs in gotestfmt pretty format outputted at complement_test_logs_gotestfmt.log (use an editor/terminal/pager that interprets ANSI colours and UTF-8 emojis)"
|
||||
#fi
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
67
complement/complement-entrypoint.sh
Normal file
67
complement/complement-entrypoint.sh
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env bash
|
||||
set -xe
|
||||
# If we have no $SERVER_NAME set, abort
|
||||
if [ -z "$SERVER_NAME" ]; then
|
||||
echo "SERVER_NAME is not set, aborting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If /complement/ca/ca.crt or /complement/ca/ca.key are missing, abort
|
||||
if [ ! -f /complement/ca/ca.crt ] || [ ! -f /complement/ca/ca.key ]; then
|
||||
echo "/complement/ca/ca.crt or /complement/ca/ca.key is missing, aborting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Add the root cert to the local trust store
|
||||
echo 'Installing Complement CA certificate to local trust store'
|
||||
cp /complement/ca/ca.crt /usr/local/share/ca-certificates/complement-ca.crt
|
||||
update-ca-certificates
|
||||
|
||||
# Sign a certificate for our $SERVER_NAME
|
||||
echo "Generating and signing certificate for $SERVER_NAME"
|
||||
openssl genrsa -out "/$SERVER_NAME.key" 2048
|
||||
|
||||
echo "Generating CSR for $SERVER_NAME"
|
||||
openssl req -new -sha256 \
|
||||
-key "/$SERVER_NAME.key" \
|
||||
-out "/$SERVER_NAME.csr" \
|
||||
-subj "/C=US/ST=CA/O=Continuwuity, Inc./CN=$SERVER_NAME"\
|
||||
-addext "subjectAltName=DNS:$SERVER_NAME"
|
||||
openssl req -in "$SERVER_NAME.csr" -noout -text
|
||||
|
||||
echo "Signing certificate for $SERVER_NAME with Complement CA"
|
||||
cat <<EOF > ./cert.ext
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = digitalSignature, keyEncipherment, dataEncipherment, nonRepudiation
|
||||
extendedKeyUsage = serverAuth
|
||||
subjectAltName = @alt_names
|
||||
[alt_names]
|
||||
DNS.1 = *.docker.internal
|
||||
DNS.2 = hs1
|
||||
DNS.3 = hs2
|
||||
DNS.4 = hs3
|
||||
DNS.5 = hs4
|
||||
DNS.6 = $SERVER_NAME
|
||||
IP.1 = 127.0.0.1
|
||||
EOF
|
||||
openssl x509 \
|
||||
-req \
|
||||
-in "/$SERVER_NAME.csr" \
|
||||
-CA /complement/ca/ca.crt \
|
||||
-CAkey /complement/ca/ca.key \
|
||||
-CAcreateserial \
|
||||
-out "/$SERVER_NAME.crt" \
|
||||
-days 1 \
|
||||
-sha256 \
|
||||
-extfile ./cert.ext
|
||||
|
||||
# Tell continuwuity where to find the certs
|
||||
export CONTINUWUITY_TLS__KEY="/$SERVER_NAME.key"
|
||||
export CONTINUWUITY_TLS__CERTS="/$SERVER_NAME.crt"
|
||||
# And who it is
|
||||
export CONTINUWUITY_SERVER_NAME="$SERVER_NAME"
|
||||
|
||||
echo "Starting Continuwuity with SERVER_NAME=$SERVER_NAME"
|
||||
# Start continuwuity
|
||||
/usr/local/bin/conduwuit --config /etc/continuwuity/config.toml
|
||||
53
complement/complement.config.toml
Normal file
53
complement/complement.config.toml
Normal file
@@ -0,0 +1,53 @@
|
||||
# ============================================= #
|
||||
# Complement pre-filled configuration file #
|
||||
#
|
||||
# DANGER: THIS FILE FORCES INSECURE VALUES. #
|
||||
# DO NOT USE OUTSIDE THE TEST SUITE ENV! #
|
||||
# ============================================= #
|
||||
[global]
|
||||
address = "0.0.0.0"
|
||||
allow_device_name_federation = true
|
||||
allow_guest_registration = true
|
||||
allow_public_room_directory_over_federation = true
|
||||
allow_public_room_directory_without_auth = true
|
||||
allow_registration = true
|
||||
database_path = "/database"
|
||||
log = "trace,h2=debug,hyper=debug"
|
||||
port = [8008, 8448]
|
||||
trusted_servers = []
|
||||
only_query_trusted_key_servers = false
|
||||
query_trusted_key_servers_first = false
|
||||
query_trusted_key_servers_first_on_join = false
|
||||
yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true
|
||||
ip_range_denylist = []
|
||||
url_preview_domain_contains_allowlist = ["*"]
|
||||
url_preview_domain_explicit_denylist = ["*"]
|
||||
media_compat_file_link = false
|
||||
media_startup_check = true
|
||||
prune_missing_media = true
|
||||
log_colors = true
|
||||
admin_room_notices = false
|
||||
allow_check_for_updates = false
|
||||
intentionally_unknown_config_option_for_testing = true
|
||||
rocksdb_log_level = "info"
|
||||
rocksdb_max_log_files = 1
|
||||
rocksdb_recovery_mode = 0
|
||||
rocksdb_paranoid_file_checks = true
|
||||
log_guest_registrations = false
|
||||
allow_legacy_media = true
|
||||
startup_netburst = true
|
||||
startup_netburst_keep = -1
|
||||
allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure = true
|
||||
dns_timeout = 60
|
||||
dns_attempts = 20
|
||||
request_conn_timeout = 60
|
||||
request_timeout = 120
|
||||
well_known_conn_timeout = 60
|
||||
well_known_timeout = 60
|
||||
federation_idle_timeout = 300
|
||||
sender_timeout = 300
|
||||
sender_idle_timeout = 300
|
||||
sender_retry_backoff_limit = 300
|
||||
|
||||
[global.tls]
|
||||
dual_protocol = true
|
||||
@@ -433,7 +433,7 @@
|
||||
# If you would like registration only via token reg, please configure
|
||||
# `registration_token`.
|
||||
#
|
||||
#allow_registration = false
|
||||
#allow_registration = true
|
||||
|
||||
# If registration is enabled, and this setting is true, new users
|
||||
# registered after the first admin user will be automatically suspended
|
||||
@@ -1474,6 +1474,10 @@
|
||||
#
|
||||
#url_preview_check_root_domain = false
|
||||
|
||||
# User agent that is used specifically when fetching url previews.
|
||||
#
|
||||
#url_preview_user_agent = "continuwuity/<version> (bot; +https://continuwuity.org)"
|
||||
|
||||
# List of forbidden room aliases and room IDs as strings of regex
|
||||
# patterns.
|
||||
#
|
||||
@@ -1759,10 +1763,6 @@
|
||||
#
|
||||
#config_reload_signal = true
|
||||
|
||||
# This item is undocumented. Please contribute documentation for it.
|
||||
#
|
||||
#ldap = false
|
||||
|
||||
[global.tls]
|
||||
|
||||
# Path to a valid TLS certificate file.
|
||||
@@ -1824,6 +1824,17 @@
|
||||
#
|
||||
#support_mxid =
|
||||
|
||||
# A list of MatrixRTC foci URLs which will be served as part of the
|
||||
# MSC4143 client endpoint at /.well-known/matrix/client. If you're
|
||||
# setting up livekit, you'd want something like:
|
||||
# rtc_focus_server_urls = [
|
||||
# { type = "livekit", livekit_service_url = "https://livekit.example.com" },
|
||||
# ]
|
||||
#
|
||||
# To disable, set this to be an empty vector (`[]`).
|
||||
#
|
||||
#rtc_focus_server_urls = []
|
||||
|
||||
[global.blurhashing]
|
||||
|
||||
# blurhashing x component, 4 is recommended by https://blurha.sh/
|
||||
@@ -1930,7 +1941,9 @@
|
||||
#
|
||||
#admin_filter = ""
|
||||
|
||||
[global.antispam.meowlnir]
|
||||
#[global.antispam]
|
||||
|
||||
#[global.antispam.meowlnir]
|
||||
|
||||
# The base URL on which to contact Meowlnir (before /_meowlnir/antispam).
|
||||
#
|
||||
@@ -1955,7 +1968,7 @@
|
||||
#
|
||||
#check_all_joins = false
|
||||
|
||||
[global.antispam.draupnir]
|
||||
#[global.antispam.draupnir]
|
||||
|
||||
# The base URL on which to contact Draupnir (before /api/).
|
||||
#
|
||||
|
||||
@@ -48,7 +48,7 @@ EOF
|
||||
|
||||
# Developer tool versions
|
||||
# renovate: datasource=github-releases depName=cargo-bins/cargo-binstall
|
||||
ENV BINSTALL_VERSION=1.16.6
|
||||
ENV BINSTALL_VERSION=1.17.5
|
||||
# renovate: datasource=github-releases depName=psastras/sbom-rs
|
||||
ENV CARGO_SBOM_VERSION=0.9.1
|
||||
# renovate: datasource=crate depName=lddtree
|
||||
@@ -162,6 +162,7 @@ ENV CONDUWUIT_VERSION_EXTRA=$CONDUWUIT_VERSION_EXTRA
|
||||
ENV CONTINUWUITY_VERSION_EXTRA=$CONTINUWUITY_VERSION_EXTRA
|
||||
|
||||
ARG RUST_PROFILE=release
|
||||
ARG CARGO_FEATURES="default,http3"
|
||||
|
||||
# Build the binary
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
@@ -171,11 +172,20 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
set -o allexport
|
||||
set -o xtrace
|
||||
. /etc/environment
|
||||
|
||||
# Check if http3 feature is enabled and set appropriate RUSTFLAGS
|
||||
if echo "${CARGO_FEATURES}" | grep -q "http3"; then
|
||||
export RUSTFLAGS="${RUSTFLAGS} --cfg reqwest_unstable"
|
||||
else
|
||||
export RUSTFLAGS="${RUSTFLAGS}"
|
||||
fi
|
||||
|
||||
TARGET_DIR=($(cargo metadata --no-deps --format-version 1 | \
|
||||
jq -r ".target_directory"))
|
||||
mkdir /out/sbin
|
||||
PACKAGE=conduwuit
|
||||
xx-cargo build --locked --profile ${RUST_PROFILE} \
|
||||
--no-default-features --features ${CARGO_FEATURES} \
|
||||
-p $PACKAGE;
|
||||
BINARIES=($(cargo metadata --no-deps --format-version 1 | \
|
||||
jq -r ".packages[] | select(.name == \"$PACKAGE\") | .targets[] | select( .kind | map(. == \"bin\") | any ) | .name"))
|
||||
|
||||
11
docker/complement.Dockerfile
Normal file
11
docker/complement.Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
FROM ubuntu:latest
|
||||
EXPOSE 8008
|
||||
EXPOSE 8448
|
||||
RUN apt-get update && apt-get install -y ca-certificates liburing2 && rm -rf /var/lib/apt/lists/*
|
||||
RUN mkdir -p /etc/continuwuity /var/lib/continuwuity /usr/local/bin/
|
||||
COPY complement/complement-entrypoint.sh /usr/local/bin/complement-entrypoint.sh
|
||||
COPY complement/complement.config.toml /etc/continuwuity/config.toml
|
||||
COPY target/debug/conduwuit /usr/local/bin/conduwuit
|
||||
RUN chmod +x /usr/local/bin/conduwuit /usr/local/bin/complement-entrypoint.sh
|
||||
#HEALTHCHECK --interval=30s --timeout=5s CMD curl --fail http://localhost:8008/_continuwuity/server_version || exit 1
|
||||
ENTRYPOINT ["/usr/local/bin/complement-entrypoint.sh"]
|
||||
@@ -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.16.6
|
||||
ENV BINSTALL_VERSION=1.17.5
|
||||
# renovate: datasource=github-releases depName=psastras/sbom-rs
|
||||
ENV CARGO_SBOM_VERSION=0.9.1
|
||||
# renovate: datasource=crate depName=lddtree
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
"label": "Deploying"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"name": "turn",
|
||||
"label": "TURN"
|
||||
"type": "dir",
|
||||
"name": "calls",
|
||||
"label": "Calls"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
@@ -34,6 +34,14 @@
|
||||
"name": "troubleshooting",
|
||||
"label": "Troubleshooting"
|
||||
},
|
||||
"security",
|
||||
{
|
||||
"type": "dir-section-header",
|
||||
"name": "community",
|
||||
"label": "Community",
|
||||
"collapsible": true,
|
||||
"collapsed": false
|
||||
},
|
||||
{
|
||||
"type": "divider"
|
||||
},
|
||||
@@ -57,18 +65,11 @@
|
||||
"name": "/reference/config"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"type": "dir",
|
||||
"label": "Admin Command Reference",
|
||||
"name": "/reference/admin"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"label": "Server Reference",
|
||||
"name": "/reference/server"
|
||||
"name": "/reference/admin/"
|
||||
},
|
||||
{
|
||||
"type": "divider"
|
||||
},
|
||||
"community",
|
||||
"security"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{
|
||||
"text": "Guide",
|
||||
"link": "/introduction",
|
||||
"activeMatch": "^/(introduction|configuration|deploying|turn|appservices|maintenance|troubleshooting)"
|
||||
"activeMatch": "^/(introduction|configuration|deploying|calls|appservices|maintenance|troubleshooting)"
|
||||
},
|
||||
{
|
||||
"text": "Development",
|
||||
@@ -18,17 +18,22 @@
|
||||
},
|
||||
{
|
||||
"text": "Admin Command Reference",
|
||||
"link": "/reference/admin"
|
||||
},
|
||||
{
|
||||
"text": "Server Reference",
|
||||
"link": "/reference/server"
|
||||
"link": "/reference/admin/"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "Community",
|
||||
"link": "/community"
|
||||
"items": [
|
||||
{
|
||||
"text": "Community Guidelines",
|
||||
"link": "/community/guidelines"
|
||||
},
|
||||
{
|
||||
"text": "Become a Partnered Homeserver!",
|
||||
"link": "/community/ops-guidelines"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "Security",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
13
docs/calls.mdx
Normal file
13
docs/calls.mdx
Normal file
@@ -0,0 +1,13 @@
|
||||
# Calls
|
||||
|
||||
Matrix supports two types of calls:
|
||||
|
||||
- Element Call powered by [MatrixRTC](https://half-shot.github.io/msc-crafter/#msc/4143) and [LiveKit](https://github.com/livekit/livekit)
|
||||
- Legacy calls, sometimes using Jitsi
|
||||
|
||||
Both types of calls are supported by different sets of clients, but most clients are moving towards MatrixRTC / Element Call.
|
||||
|
||||
For either one to work correctly, you have to do some additional setup.
|
||||
|
||||
- For legacy calls to work, you need to set up a TURN/STUN server. [Read the TURN guide for tips on how to set up coturn](./calls/turn.mdx)
|
||||
- For MatrixRTC / Element Call to work, you have to set up the LiveKit backend (foci). LiveKit also uses TURN/STUN to increase reliability, so you might want to configure your TURN server first. [Read the LiveKit guide](./calls/livekit.mdx)
|
||||
12
docs/calls/_meta.json
Normal file
12
docs/calls/_meta.json
Normal file
@@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"type": "file",
|
||||
"name": "turn",
|
||||
"label": "TURN"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"name": "livekit",
|
||||
"label": "MatrixRTC / LiveKit"
|
||||
}
|
||||
]
|
||||
229
docs/calls/livekit.mdx
Normal file
229
docs/calls/livekit.mdx
Normal file
@@ -0,0 +1,229 @@
|
||||
# Matrix RTC/Element Call Setup
|
||||
|
||||
:::info
|
||||
This guide assumes that you are using docker compose for deployment. LiveKit only provides Docker images.
|
||||
:::
|
||||
|
||||
## Instructions
|
||||
|
||||
### 1. Domain
|
||||
|
||||
LiveKit should live on its own domain or subdomain. In this guide we use `livekit.example.com` - this should be replaced with a domain you control.
|
||||
|
||||
Make sure the DNS record for the (sub)domain you plan to use is pointed to your server.
|
||||
|
||||
### 2. Services
|
||||
|
||||
Using LiveKit with Matrix requires two services - Livekit itself, and a service (`lk-jwt-service`) that grants Matrix users permission to connect to it.
|
||||
|
||||
You must generate a key and secret to allow the Matrix service to authenticate with LiveKit. `LK_MATRIX_KEY` should be around 20 random characters, and `LK_MATRIX_SECRET` should be around 64. Remember to replace these with the actual values!
|
||||
|
||||
:::tip Generating the secrets
|
||||
LiveKit provides a utility to generate secure random keys
|
||||
```bash
|
||||
docker run --rm livekit/livekit-server:latest generate-keys
|
||||
```
|
||||
:::
|
||||
|
||||
```yaml
|
||||
services:
|
||||
lk-jwt-service:
|
||||
image: ghcr.io/element-hq/lk-jwt-service:latest
|
||||
container_name: lk-jwt-service
|
||||
environment:
|
||||
- LIVEKIT_JWT_BIND=:8081
|
||||
- LIVEKIT_URL=wss://livekit.example.com
|
||||
- LIVEKIT_KEY=LK_MATRIX_KEY
|
||||
- LIVEKIT_SECRET=LK_MATRIX_SECRET
|
||||
- LIVEKIT_FULL_ACCESS_HOMESERVERS=example.com
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8081:8081"
|
||||
|
||||
livekit:
|
||||
image: livekit/livekit-server:latest
|
||||
container_name: livekit
|
||||
command: --config /etc/livekit.yaml
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./livekit.yaml:/etc/livekit.yaml:ro
|
||||
network_mode: "host" # /!\ LiveKit binds to all addresses by default.
|
||||
# Make sure port 7880 is blocked by your firewall to prevent access bypassing your reverse proxy
|
||||
# Alternatively, uncomment the lines below and comment `network_mode: "host"` above to specify port mappings.
|
||||
# ports:
|
||||
# - "127.0.0.1:7880:7880/tcp"
|
||||
# - "7881:7881/tcp"
|
||||
# - "50100-50200:50100-50200/udp"
|
||||
```
|
||||
|
||||
Next, we need to configure LiveKit. In the same directory, create `livekit.yaml` with the following content - remembering to replace `LK_MATRIX_KEY` and `LK_MATRIX_SECRET` with the values you generated:
|
||||
|
||||
```yaml
|
||||
port: 7880
|
||||
bind_addresses:
|
||||
- ""
|
||||
rtc:
|
||||
tcp_port: 7881
|
||||
port_range_start: 50100
|
||||
port_range_end: 50200
|
||||
use_external_ip: true
|
||||
enable_loopback_candidate: false
|
||||
keys:
|
||||
LK_MATRIX_KEY: LK_MATRIX_SECRET
|
||||
```
|
||||
|
||||
#### Firewall hints
|
||||
|
||||
You will need to allow ports `7881/tcp` and `50100:50200/udp` through your firewall. If you use UFW, the commands are: `ufw allow 7881/tcp` and `ufw allow 50100:50200/udp`.
|
||||
|
||||
### 3. Telling clients where to find LiveKit
|
||||
|
||||
To tell clients where to find LiveKit, you need to add the address of your `lk-jwt-service` to your client .well-known file. To do so, in the config section `global.well-known`, add (or modify) the option `rtc_focus_server_urls`.
|
||||
|
||||
The variable should be a list of servers serving as MatrixRTC endpoints to serve in the well-known file to the client.
|
||||
|
||||
```toml
|
||||
rtc_focus_server_urls = [
|
||||
{ type = "livekit", livekit_service_url = "https://livekit.example.com" },
|
||||
]
|
||||
```
|
||||
|
||||
Remember to replace the URL with the address you are deploying your instance of lk-jwt-service to.
|
||||
|
||||
#### Serving .well-known manually
|
||||
|
||||
If you don't let Continuwuity serve your `.well-known` files, you need to add the following lines to your `.well-known/matrix/client` file, remembering to replace the URL with your own `lk-jwt-service` deployment:
|
||||
|
||||
```json
|
||||
"org.matrix.msc4143.rtc_foci": [
|
||||
{
|
||||
"type": "livekit",
|
||||
"livekit_service_url": "https://livekit.example.com"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
The final file should look something like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"m.homeserver": {
|
||||
"base_url":"https://matrix.example.com"
|
||||
},
|
||||
"org.matrix.msc4143.rtc_foci": [
|
||||
{
|
||||
"type": "livekit",
|
||||
"livekit_service_url": "https://livekit.example.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Configure your Reverse Proxy
|
||||
|
||||
Reverse proxies can be configured in many different ways - so we can't provide a step by step for this.
|
||||
|
||||
By default, all routes should be forwarded to Livekit with the exception of the following path prefixes, which should be forwarded to the JWT/Authentication service:
|
||||
|
||||
- `/sfu/get`
|
||||
- `/healthz`
|
||||
- `/get_token`
|
||||
|
||||
<details>
|
||||
<summary>Example caddy config</summary>
|
||||
```
|
||||
matrix-rtc.example.com {
|
||||
|
||||
# for lk-jwt-service
|
||||
@lk-jwt-service path /sfu/get* /healthz* /get_token*
|
||||
route @lk-jwt-service {
|
||||
reverse_proxy 127.0.0.1:8080
|
||||
}
|
||||
|
||||
# for livekit
|
||||
reverse_proxy 127.0.0.1:7880
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Example traefik router</summary>
|
||||
```
|
||||
# on LiveKit itself
|
||||
traefik.http.routers.livekit.rule=Host(`livekit.example.com`)
|
||||
# on the JWT service
|
||||
traefik.http.routers.livekit-jwt.rule=Host(`livekit.example.com`) && (PathPrefix(`/sfu/get`) || PathPrefix(`/healthz`) || PathPrefix(`/get_token`))
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
### 6. Start Everything
|
||||
|
||||
Start up the services using your usual method - for example `docker compose up -d`.
|
||||
|
||||
## Additional Configuration
|
||||
|
||||
### TURN Integration
|
||||
|
||||
If you've already set up coturn, there may be a port clash between the two services. To fix this, make sure the `min-port` and `max-port` for coturn so it doesn't overlap with LiveKit's range:
|
||||
|
||||
```ini
|
||||
min-port=50201
|
||||
max-port=65535
|
||||
```
|
||||
|
||||
To improve LiveKit's reliability, you can configure it to use your coturn server.
|
||||
|
||||
Generate a long random secret for LiveKit, and add it to your coturn config under the `static-auth-secret` option. You can add as many secrets as you want - so set a different one for each thing using your TURN server.
|
||||
|
||||
Then configure livekit, making sure to replace `COTURN_SECRET`:
|
||||
|
||||
```yaml
|
||||
# livekit.yaml
|
||||
rtc:
|
||||
turn_servers:
|
||||
- host: coturn.ellis.link
|
||||
port: 3478
|
||||
protocol: tcp
|
||||
secret: "COTURN_SECRET"
|
||||
- host: coturn.ellis.link
|
||||
port: 5349
|
||||
protocol: tls # Only if you've set up TLS in your coturn
|
||||
secret: "COTURN_SECRET"
|
||||
- host: coturn.ellis.link
|
||||
port: 3478
|
||||
protocol: udp
|
||||
secret: "COTURN_SECRET"
|
||||
```
|
||||
|
||||
## LiveKit's built in TURN server
|
||||
|
||||
Livekit includes a built in TURN server which can be used in place of an external option. This TURN server will only work with Livekit, so you can't use it for legacy Matrix calling - or anything else.
|
||||
|
||||
If you don't want to set up a separate TURN server, you can enable this with the following changes:
|
||||
|
||||
```yaml
|
||||
### add this to livekit.yaml ###
|
||||
turn:
|
||||
enabled: true
|
||||
udp_port: 3478
|
||||
relay_range_start: 50300
|
||||
relay_range_end: 50400
|
||||
domain: matrix-rtc.example.com
|
||||
```
|
||||
|
||||
```yaml
|
||||
### Add these to docker-compose ###
|
||||
- "3478:3478/udp"
|
||||
- "50300-50400:50300-50400/udp"
|
||||
```
|
||||
|
||||
### Related Documentation
|
||||
|
||||
- [LiveKit GitHub](https://github.com/livekit/livekit)
|
||||
- [LiveKit Connection Tester](https://livekit.io/connection-test) - use with the token returned by `/sfu/get` or `/get_token`
|
||||
- [MatrixRTC proposal](https://half-shot.github.io/msc-crafter/#msc/4143)
|
||||
- [Synapse documentation](https://github.com/element-hq/element-call/blob/livekit/docs/self-hosting.md)
|
||||
- [Community guide](https://tomfos.tr/matrix/livekit/)
|
||||
- [Community guide](https://blog.kimiblock.top/2024/12/24/hosting-element-call/)
|
||||
-
|
||||
214
docs/calls/turn.mdx
Normal file
214
docs/calls/turn.mdx
Normal file
@@ -0,0 +1,214 @@
|
||||
# Setting up TURN/STUN
|
||||
|
||||
[TURN](https://en.wikipedia.org/wiki/Traversal_Using_Relays_around_NAT) and [STUN](https://en.wikipedia.org/wiki/STUN) are used as a component in many calling systems. Matrix uses them directly for legacy calls and indirectly for MatrixRTC via Livekit.
|
||||
|
||||
Continuwuity recommends using [Coturn](https://github.com/coturn/coturn) as your TURN/STUN server, which is available as a Docker image or a distro package.
|
||||
|
||||
## Installing Coturn
|
||||
|
||||
### Configuration
|
||||
|
||||
Create a configuration file called `coturn.conf` containing:
|
||||
|
||||
```ini
|
||||
use-auth-secret
|
||||
static-auth-secret=<a secret key>
|
||||
realm=<your server domain>
|
||||
```
|
||||
|
||||
:::tip Generating a secure secret
|
||||
A common way to generate a suitable alphanumeric secret key is by using:
|
||||
```bash
|
||||
pwgen -s 64 1
|
||||
```
|
||||
:::
|
||||
|
||||
#### Port Configuration
|
||||
|
||||
By default, coturn uses the following ports:
|
||||
- `3478` (UDP/TCP): Standard TURN/STUN port
|
||||
- `5349` (UDP/TCP): TURN/STUN over TLS
|
||||
- `49152-65535` (UDP): Media relay ports
|
||||
|
||||
If you're also running LiveKit, you'll need to avoid port conflicts. Configure non-overlapping port ranges:
|
||||
|
||||
```ini
|
||||
# In coturn.conf
|
||||
min-port=50201
|
||||
max-port=65535
|
||||
```
|
||||
|
||||
This leaves ports `50100-50200` available for LiveKit's default configuration.
|
||||
|
||||
### Running with Docker
|
||||
|
||||
Run the [Coturn](https://hub.docker.com/r/coturn/coturn) image using:
|
||||
|
||||
```bash
|
||||
docker run -d --network=host \
|
||||
-v $(pwd)/coturn.conf:/etc/coturn/turnserver.conf \
|
||||
coturn/coturn
|
||||
```
|
||||
|
||||
### Running with Docker Compose
|
||||
|
||||
Create a `docker-compose.yml` file and run `docker compose up -d`:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
services:
|
||||
turn:
|
||||
container_name: coturn-server
|
||||
image: docker.io/coturn/coturn
|
||||
restart: unless-stopped
|
||||
network_mode: "host"
|
||||
volumes:
|
||||
- ./coturn.conf:/etc/coturn/turnserver.conf
|
||||
```
|
||||
|
||||
:::info Why host networking?
|
||||
Coturn uses host networking mode because it needs to bind to multiple ports and work with various network protocols. Using host networking is better for performance, and reduces configuration complexity. To understand alternative configuration options, visit [Coturn's Docker documentation](https://github.com/coturn/coturn/blob/master/docker/coturn/README.md).
|
||||
:::
|
||||
|
||||
### Security Recommendations
|
||||
|
||||
For security best practices, see Synapse's [Coturn documentation](https://element-hq.github.io/synapse/latest/turn-howto.html), which includes important firewall and access control recommendations.
|
||||
|
||||
## Configuring Continuwuity
|
||||
|
||||
Once your TURN server is running, configure Continuwuity to provide credentials to clients. Add the following to your Continuwuity configuration file:
|
||||
|
||||
### Shared Secret Authentication (Recommended)
|
||||
|
||||
This is the most secure method and generates time-limited credentials automatically:
|
||||
|
||||
```toml
|
||||
# TURN URIs that clients should connect to
|
||||
turn_uris = [
|
||||
"turn:coturn.example.com?transport=udp",
|
||||
"turn:coturn.example.com?transport=tcp",
|
||||
"turns:coturn.example.com?transport=udp",
|
||||
"turns:coturn.example.com?transport=tcp"
|
||||
]
|
||||
|
||||
# Shared secret for generating credentials (must match coturn's static-auth-secret)
|
||||
turn_secret = "<your coturn static-auth-secret>"
|
||||
|
||||
# Optional: Read secret from a file instead (takes priority over turn_secret)
|
||||
# turn_secret_file = "/etc/continuwuity/.turn_secret"
|
||||
|
||||
# TTL for generated credentials in seconds (default: 86400 = 24 hours)
|
||||
turn_ttl = 86400
|
||||
```
|
||||
|
||||
:::tip Using TLS
|
||||
The `turns:` URI prefix instructs clients to connect to TURN over TLS, which is highly recommended for security. Make sure you've configured TLS in your coturn server first.
|
||||
:::
|
||||
|
||||
### Static Credentials (Alternative)
|
||||
|
||||
If you prefer static username/password credentials instead of shared secrets:
|
||||
|
||||
```toml
|
||||
turn_uris = [
|
||||
"turn:coturn.example.com?transport=udp",
|
||||
"turn:coturn.example.com?transport=tcp"
|
||||
]
|
||||
|
||||
turn_username = "your_username"
|
||||
turn_password = "your_password"
|
||||
```
|
||||
|
||||
:::warning
|
||||
Static credentials are less secure than shared secrets because they don't expire and must be configured in coturn separately. It is strongly advised you use shared secret authentication.
|
||||
:::
|
||||
|
||||
### Guest Access
|
||||
|
||||
By default, TURN credentials require client authentication. To allow unauthenticated access:
|
||||
|
||||
```toml
|
||||
turn_allow_guests = true
|
||||
```
|
||||
|
||||
:::caution
|
||||
This is not recommended as it allows unauthenticated users to access your TURN server, potentially enabling abuse by bots. All major Matrix clients that support legacy calls *also* support authenticated TURN access.
|
||||
:::
|
||||
|
||||
### Important Notes
|
||||
|
||||
- Replace `coturn.example.com` with your actual TURN server domain (the `realm` from coturn.conf)
|
||||
- The `turn_secret` must match the `static-auth-secret` in your coturn configuration
|
||||
- Restart or reload Continuwuity after making configuration changes
|
||||
|
||||
## Testing Your TURN Server
|
||||
|
||||
### Testing Credentials
|
||||
|
||||
Verify that Continuwuity is correctly serving TURN credentials to clients:
|
||||
|
||||
```bash
|
||||
curl "https://matrix.example.com/_matrix/client/r0/voip/turnServer" \
|
||||
-H "Authorization: Bearer <your_client_token>" | jq
|
||||
```
|
||||
|
||||
You should receive 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
|
||||
}
|
||||
```
|
||||
|
||||
:::note MSC4166 Compliance
|
||||
If no TURN URIs are configured (`turn_uris` is empty), Continuwuity will return a 404 Not Found response, as specified in MSC4166.
|
||||
:::
|
||||
|
||||
### Testing Connectivity
|
||||
|
||||
Use [Trickle ICE](https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/) to verify that the TURN credentials actually work:
|
||||
|
||||
1. Copy the credentials from the response above
|
||||
2. Paste them into the Trickle ICE testing tool
|
||||
3. Click "Gather candidates"
|
||||
4. Look for successful `relay` candidates in the results
|
||||
|
||||
If you see relay candidates, your TURN server is working correctly!
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Clients can't connect to TURN server
|
||||
|
||||
- Verify firewall rules allow the necessary ports (3478, 5349, and your media port range)
|
||||
- Check that DNS resolves correctly for your TURN domain
|
||||
- Ensure your `turn_secret` matches coturn's `static-auth-secret`
|
||||
- Test with Trickle ICE to isolate the issue
|
||||
|
||||
### Port conflicts with LiveKit
|
||||
|
||||
- Make sure coturn's `min-port` starts above LiveKit's `port_range_end` (default: 50200)
|
||||
- Or adjust LiveKit's port range to avoid coturn's default range
|
||||
|
||||
### 404 when calling turnServer endpoint
|
||||
|
||||
- Verify that `turn_uris` is not empty in your Continuwuity config
|
||||
- This behavior is correct per MSC4166 if no TURN URIs are configured
|
||||
|
||||
### Credentials expire too quickly
|
||||
|
||||
- Adjust the `turn_ttl` value in your Continuwuity configuration
|
||||
- Default is 86400 seconds (24 hours)
|
||||
|
||||
### Related Documentation
|
||||
|
||||
- [MatrixRTC/LiveKit Setup](./livekit.mdx) - Configure group calling with LiveKit
|
||||
- [Coturn GitHub](https://github.com/coturn/coturn) - Official coturn repository
|
||||
- [Synapse TURN Guide](https://element-hq.github.io/synapse/latest/turn-howto.html) - Additional security recommendations
|
||||
12
docs/community/_meta.json
Normal file
12
docs/community/_meta.json
Normal file
@@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"type": "file",
|
||||
"name": "guidelines",
|
||||
"label": "Community Guidelines"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"name": "ops-guidelines",
|
||||
"label": "Partnered Homeserver Guidelines"
|
||||
}
|
||||
]
|
||||
32
docs/community/ops-guidelines.mdx
Normal file
32
docs/community/ops-guidelines.mdx
Normal file
@@ -0,0 +1,32 @@
|
||||
# Partnered Homeserver Operator Requirements
|
||||
> _So you want to be an officially sanctioned public Continuwuity homeserver operator?_
|
||||
|
||||
Thank you for your interest in the project! There's a few things we need from you first to make sure your homeserver meets our quality standards and that you are prepared to handle the additional workload introduced by operating a public chat service.
|
||||
|
||||
## Stuff you must have
|
||||
if you don't do these things we will tell you to go away
|
||||
|
||||
- Your homeserver must be running an up-to-date version of Continuwuity
|
||||
- You must have a CAPTCHA, external registration system, or apply-to-join system that provides one-time-use invite codes (we do not accept fully open nor static token registration)
|
||||
- Your homeserver must have support details listed in [`/.well-known/matrix/support`](https://spec.matrix.org/v1.17/client-server-api/#getwell-knownmatrixsupport)
|
||||
- Your rules and guidelines must align with [the project's own code of conduct](guidelines).
|
||||
- You must be reasonably responsive (i.e. don't leave us hanging for a week if we alert you to an issue on your server)
|
||||
- Your homeserver's community rooms (if any) must be protected by a moderation bot subscribed to policy lists like the Community Moderation Effort (you can get one from https://asgard.chat if you don't want to run your own)
|
||||
|
||||
## Stuff we encourage you to have
|
||||
not strictly required but we will consider your request more strongly if you have it
|
||||
|
||||
- You should have automated moderation tooling that can automatically suspend abusive users on your homeserver who are added to policy lists
|
||||
- You should have multiple server administrators (increased bus factor)
|
||||
- You should have a terms of service and privacy policy prominently available
|
||||
|
||||
## Stuff you get
|
||||
|
||||
- Prominent listing in our README!
|
||||
- A gold star sticker
|
||||
- Access to a low noise room for more direct communication with maintainers and collaboration with fellow operators
|
||||
- Read-only access to the continuwuity internal ban list
|
||||
- Early notice of upcoming releases
|
||||
|
||||
## Sound good?
|
||||
To get started, ping a team member in [our main chatroom](https://matrix.to/#/#continuwuity:continuwuity.org) and ask to be added to the list.
|
||||
@@ -13,8 +13,8 @@ ### Use a registry
|
||||
| --------------- | --------------------------------------------------------------- | -----------------------|
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:latest](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/latest) | Latest tagged image. |
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:main](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/main) | Main branch image. |
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:latest-maxperf](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/latest-maxperf) | Performance optimised version. |
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:main-maxperf](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/main-maxperf) | Performance optimised version. |
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:latest-maxperf](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/latest-maxperf) | [Performance optimised version.](./generic.mdx#performance-optimised-builds) |
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:main-maxperf](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/main-maxperf) | [Performance optimised version.](./generic.mdx#performance-optimised-builds) |
|
||||
|
||||
Use
|
||||
|
||||
@@ -217,4 +217,4 @@ ### Use Traefik as Proxy
|
||||
|
||||
## Voice communication
|
||||
|
||||
See the [TURN](../turn.md) page.
|
||||
See the [Calls](../calls.mdx) page.
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
# RPM Installation Guide
|
||||
|
||||
Continuwuity is available as RPM packages for Fedora, RHEL, and compatible distributions.
|
||||
Continuwuity is available as RPM packages for Fedora and compatible distributions.
|
||||
We do not currently have infrastructure to build RPMs for RHEL and compatible distributions, but this is a work in progress.
|
||||
|
||||
The RPM packaging files are maintained in the `fedora/` directory:
|
||||
- `continuwuity.spec.rpkg` - RPM spec file using rpkg macros for building from git
|
||||
- `continuwuity.service` - Systemd service file for the server
|
||||
- `RPM-GPG-KEY-continuwuity.asc` - GPG public key for verifying signed packages
|
||||
|
||||
RPM packages built by CI are signed with our GPG key (Ed25519, ID: `5E0FF73F411AAFCA`).
|
||||
RPM packages built by CI are signed with our GPG key (RSA, ID: `6595 E8DB 9191 D39A 46D6 A514 4BA7 F590 DF0B AA1D`). # spellchecker:disable-line
|
||||
|
||||
```bash
|
||||
# Import the signing key
|
||||
sudo rpm --import https://forgejo.ellis.link/continuwuation/continuwuity/raw/branch/main/fedora/RPM-GPG-KEY-continuwuity.asc
|
||||
sudo rpm --import https://forgejo.ellis.link/api/packages/continuwuation/rpm/repository.key
|
||||
|
||||
# Verify a downloaded package
|
||||
rpm --checksig continuwuity-*.rpm
|
||||
@@ -23,7 +24,7 @@ ## Installation methods
|
||||
|
||||
```bash
|
||||
# Add the repository and install
|
||||
sudo dnf config-manager addrepo --from-repofile=https://forgejo.ellis.link/api/packages/continuwuation/rpm/stable/continuwuation.repo
|
||||
sudo dnf config-manager addrepo --from-repofile=https://forgejo.ellis.link/api/packages/continuwuation/rpm/stable.repo
|
||||
sudo dnf install continuwuity
|
||||
```
|
||||
|
||||
@@ -31,7 +32,7 @@ # Add the repository and install
|
||||
|
||||
```bash
|
||||
# Add the dev repository and install
|
||||
sudo dnf config-manager addrepo --from-repofile=https://forgejo.ellis.link/api/packages/continuwuation/rpm/dev/continuwuation.repo
|
||||
sudo dnf config-manager addrepo --from-repofile=https://forgejo.ellis.link/api/packages/continuwuation/rpm/dev.repo
|
||||
sudo dnf install continuwuity
|
||||
```
|
||||
|
||||
@@ -39,23 +40,10 @@ # Add the dev repository and install
|
||||
|
||||
```bash
|
||||
# Branch names are sanitized (slashes become hyphens, lowercase only)
|
||||
sudo dnf config-manager addrepo --from-repofile=https://forgejo.ellis.link/api/packages/continuwuation/rpm/tom-new-feature/continuwuation.repo
|
||||
sudo dnf config-manager addrepo --from-repofile=https://forgejo.ellis.link/api/packages/continuwuation/rpm/tom-new-feature.repo
|
||||
sudo dnf install continuwuity
|
||||
```
|
||||
|
||||
**Direct installation** without adding repository
|
||||
|
||||
```bash
|
||||
# Latest stable release
|
||||
sudo dnf install https://forgejo.ellis.link/api/packages/continuwuation/rpm/stable/continuwuity
|
||||
|
||||
# Latest development build
|
||||
sudo dnf install https://forgejo.ellis.link/api/packages/continuwuation/rpm/dev/continuwuity
|
||||
|
||||
# Specific feature branch
|
||||
sudo dnf install https://forgejo.ellis.link/api/packages/continuwuation/rpm/branch-name/continuwuity
|
||||
```
|
||||
|
||||
**Manual repository configuration** (alternative method)
|
||||
|
||||
```bash
|
||||
@@ -65,7 +53,7 @@ # Specific feature branch
|
||||
baseurl=https://forgejo.ellis.link/api/packages/continuwuation/rpm/stable
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
gpgkey=https://forgejo.ellis.link/continuwuation/continuwuity/raw/branch/main/fedora/RPM-GPG-KEY-continuwuity.asc
|
||||
gpgkey=https://forgejo.ellis.link/api/packages/continuwuation/rpm/repository.key
|
||||
EOF
|
||||
|
||||
sudo dnf install continuwuity
|
||||
|
||||
@@ -56,6 +56,8 @@ ### Building with the Rust toolchain
|
||||
|
||||
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.
|
||||
@@ -269,7 +271,7 @@ # If federation is enabled
|
||||
```
|
||||
|
||||
- To check if your server can communicate with other homeservers, use the
|
||||
[Matrix Federation Tester](https://federationtester.matrix.org/). If you can
|
||||
[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.
|
||||
|
||||
@@ -277,7 +279,7 @@ # What's next?
|
||||
|
||||
## Audio/Video calls
|
||||
|
||||
For Audio/Video call functionality see the [TURN Guide](../turn.md).
|
||||
For Audio/Video call functionality see the [Calls](../calls.md) page.
|
||||
|
||||
## Appservices
|
||||
|
||||
|
||||
@@ -19,6 +19,16 @@
|
||||
src: /assets/logo.svg
|
||||
alt: continuwuity logo
|
||||
|
||||
beforeFeatures:
|
||||
- title: Matrix for Discord users
|
||||
details: New to Matrix? Learn how Matrix compares to Discord
|
||||
link: https://joinmatrix.org/guide/matrix-vs-discord/
|
||||
buttonText: Find Out the Difference
|
||||
- title: How Matrix Works
|
||||
details: Learn how Matrix works under the hood, and what that means
|
||||
link: https://matrix.org/docs/matrix-concepts/elements-of-matrix/
|
||||
buttonText: Read the Guide
|
||||
|
||||
features:
|
||||
- title: 🚀 High Performance
|
||||
details: Built with Rust for exceptional speed and efficiency. Designed to run smoothly even on modest hardware.
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
"message": "Welcome to Continuwuity! Important announcements about the project will appear here."
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"mention_room": true,
|
||||
"date": "2025-12-30",
|
||||
"message": "Continuwuity v0.5.1 has been released. **The release contains a fix for the critical vulnerability [GHSA-m5p2-vccg-8c9v](https://github.com/continuwuity/continuwuity/security/advisories/GHSA-m5p2-vccg-8c9v) (embargoed) affecting all Conduit-derived servers. Update as soon as possible.**\n\nThis has been *actively exploited* to attempt account takeover and forge events bricking the Continuwuity rooms. The new space is accessible at [Continuwuity (room list)](https://matrix.to/#/!8cR4g-i9ucof69E4JHNg9LbPVkGprHb3SzcrGBDDJgk?via=continuwuity.org&via=starstruck.systems&via=gingershaped.computer)\n"
|
||||
"id": 9,
|
||||
"mention_room": false,
|
||||
"date": "2026-02-09",
|
||||
"message": "Yesterday we released [v0.5.4](https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v0.5.4). Bugfixes, performance improvements and more moderation features! There's also a security fix, so please update as soon as possible. Don't forget to join [our announcements channel](https://matrix.to/#/!jIdNjSM5X-V5JVx2h2kAhUZIIQ08GyzPL55NFZAH1vM/%2489TY9CqRg4-ff1MGo3Ulc5r5X4pakfdzT-99RD8Docc?via=ellis.link&via=explodie.org&via=matrix.org) to get important information sooner <3 "
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,10 +8,5 @@
|
||||
"type": "file",
|
||||
"name": "admin",
|
||||
"label": "Admin Commands"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"name": "server",
|
||||
"label": "Server command"
|
||||
}
|
||||
]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
29
docs/reference/admin/appservices.md
Normal file
29
docs/reference/admin/appservices.md
Normal file
@@ -0,0 +1,29 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin appservices`
|
||||
|
||||
Commands for managing appservices
|
||||
|
||||
|
||||
## `!admin appservices register`
|
||||
|
||||
Register an appservice using its registration YAML
|
||||
|
||||
This command needs a YAML generated by an appservice (such as a bridge), which must be provided in a Markdown code block below the command.
|
||||
|
||||
Registering a new bridge using the ID of an existing bridge will replace the old one.
|
||||
|
||||
## `!admin appservices unregister`
|
||||
|
||||
Unregister an appservice using its ID
|
||||
|
||||
You can find the ID using the `list-appservices` command.
|
||||
|
||||
## `!admin appservices show-appservice-config`
|
||||
|
||||
Show an appservice's config using its ID
|
||||
|
||||
You can find the ID using the `list-appservices` command.
|
||||
|
||||
## `!admin appservices list-registered`
|
||||
|
||||
List all the currently registered appservices
|
||||
9
docs/reference/admin/check.md
Normal file
9
docs/reference/admin/check.md
Normal file
@@ -0,0 +1,9 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin check`
|
||||
|
||||
Commands for checking integrity
|
||||
|
||||
|
||||
## `!admin check check-all-users`
|
||||
|
||||
Uses the iterator in `src/database/key_value/users.rs` to iterator over every user in our database (remote and local). Reports total count, any errors if there were any, etc
|
||||
135
docs/reference/admin/debug.md
Normal file
135
docs/reference/admin/debug.md
Normal file
@@ -0,0 +1,135 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin debug`
|
||||
|
||||
Commands for debugging things
|
||||
|
||||
|
||||
## `!admin debug echo`
|
||||
|
||||
Echo input of admin command
|
||||
|
||||
## `!admin debug get-auth-chain`
|
||||
|
||||
Get the auth_chain of a PDU
|
||||
|
||||
## `!admin debug parse-pdu`
|
||||
|
||||
Parse and print a PDU from a JSON
|
||||
|
||||
The PDU event is only checked for validity and is not added to the database.
|
||||
|
||||
This command needs a JSON blob provided in a Markdown code block below the command.
|
||||
|
||||
## `!admin debug get-pdu`
|
||||
|
||||
Retrieve and print a PDU by EventID from the Continuwuity database
|
||||
|
||||
## `!admin debug get-short-pdu`
|
||||
|
||||
Retrieve and print a PDU by PduId from the Continuwuity database
|
||||
|
||||
## `!admin debug get-remote-pdu`
|
||||
|
||||
Attempts to retrieve a PDU from a remote server. **Does not** insert it into the database or persist it anywhere
|
||||
|
||||
## `!admin debug get-remote-pdu-list`
|
||||
|
||||
Same as `get-remote-pdu` but accepts a codeblock newline delimited list of PDUs and a single server to fetch from
|
||||
|
||||
## `!admin debug get-room-state`
|
||||
|
||||
Gets all the room state events for the specified room.
|
||||
|
||||
This is functionally equivalent to `GET /_matrix/client/v3/rooms/{roomid}/state`, except the admin command does *not* check if the sender user is allowed to see state events. This is done because it's implied that server admins here have database access and can see/get room info themselves anyways if they were malicious admins.
|
||||
|
||||
Of course the check is still done on the actual client API.
|
||||
|
||||
## `!admin debug get-signing-keys`
|
||||
|
||||
Get and display signing keys from local cache or remote server
|
||||
|
||||
## `!admin debug get-verify-keys`
|
||||
|
||||
Get and display signing keys from local cache or remote server
|
||||
|
||||
## `!admin debug ping`
|
||||
|
||||
Sends a federation request to the remote server's `/_matrix/federation/v1/version` endpoint and measures the latency it took for the server to respond
|
||||
|
||||
## `!admin debug force-device-list-updates`
|
||||
|
||||
Forces device lists for all local and remote users to be updated (as having new keys available)
|
||||
|
||||
## `!admin debug change-log-level`
|
||||
|
||||
Change tracing log level/filter on the fly
|
||||
|
||||
This accepts the same format as the `log` config option.
|
||||
|
||||
## `!admin debug verify-json`
|
||||
|
||||
Verify JSON signatures
|
||||
|
||||
This command needs a JSON blob provided in a Markdown code block below the command.
|
||||
|
||||
## `!admin debug verify-pdu`
|
||||
|
||||
Verify PDU
|
||||
|
||||
This re-verifies a PDU existing in the database found by ID.
|
||||
|
||||
## `!admin debug first-pdu-in-room`
|
||||
|
||||
Prints the very first PDU in the specified room (typically m.room.create)
|
||||
|
||||
## `!admin debug latest-pdu-in-room`
|
||||
|
||||
Prints the latest ("last") PDU in the specified room (typically a message)
|
||||
|
||||
## `!admin debug force-set-room-state-from-server`
|
||||
|
||||
Forcefully replaces the room state of our local copy of the specified room, with the copy (auth chain and room state events) the specified remote server says.
|
||||
|
||||
A common desire for room deletion is to simply "reset" our copy of the room. While this admin command is not a replacement for that, if you know you have split/broken room state and you know another server in the room that has the best/working room state, this command can let you use their room state. Such example is your server saying users are in a room, but other servers are saying they're not in the room in question.
|
||||
|
||||
This command will get the latest PDU in the room we know about, and request the room state at that point in time via `/_matrix/federation/v1/state/{roomId}`.
|
||||
|
||||
## `!admin debug resolve-true-destination`
|
||||
|
||||
Runs a server name through Continuwuity's true destination resolution process
|
||||
|
||||
Useful for debugging well-known issues
|
||||
|
||||
## `!admin debug memory-stats`
|
||||
|
||||
Print extended memory usage
|
||||
|
||||
Optional argument is a character mask (a sequence of characters in any order) which enable additional extended statistics. Known characters are "abdeglmx". For convenience, a '*' will enable everything.
|
||||
|
||||
## `!admin debug runtime-metrics`
|
||||
|
||||
Print general tokio runtime metric totals
|
||||
|
||||
## `!admin debug runtime-interval`
|
||||
|
||||
Print detailed tokio runtime metrics accumulated since last command invocation
|
||||
|
||||
## `!admin debug time`
|
||||
|
||||
Print the current time
|
||||
|
||||
## `!admin debug database-stats`
|
||||
|
||||
Get database statistics
|
||||
|
||||
## `!admin debug trim-memory`
|
||||
|
||||
Trim memory usage
|
||||
|
||||
## `!admin debug database-files`
|
||||
|
||||
List database files
|
||||
|
||||
## `!admin debug tester`
|
||||
|
||||
Developer test stubs
|
||||
29
docs/reference/admin/federation.md
Normal file
29
docs/reference/admin/federation.md
Normal file
@@ -0,0 +1,29 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin federation`
|
||||
|
||||
Commands for managing federation
|
||||
|
||||
|
||||
## `!admin federation incoming-federation`
|
||||
|
||||
List all rooms we are currently handling an incoming pdu from
|
||||
|
||||
## `!admin federation disable-room`
|
||||
|
||||
Disables incoming federation handling for a room
|
||||
|
||||
## `!admin federation enable-room`
|
||||
|
||||
Enables incoming federation handling for a room again
|
||||
|
||||
## `!admin federation fetch-support-well-known`
|
||||
|
||||
Fetch `/.well-known/matrix/support` from the specified server
|
||||
|
||||
Despite the name, this is not a federation endpoint and does not go through the federation / server resolution process as per-spec this is supposed to be served at the server_name.
|
||||
|
||||
Respecting homeservers put this file here for listing administration, moderation, and security inquiries. This command provides a way to easily fetch that information.
|
||||
|
||||
## `!admin federation remote-user-in-rooms`
|
||||
|
||||
Lists all the rooms we share/track with the specified *remote* user
|
||||
23
docs/reference/admin/index.md
Normal file
23
docs/reference/admin/index.md
Normal file
@@ -0,0 +1,23 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# Admin Command Reference
|
||||
|
||||
Admin commands allow server administrators to manage the server from within their Matrix client. "Server administrators" by default means only those users which are members of the admin room, but additional server admins may be added using the `admins_list` configuration option.
|
||||
|
||||
## Running commands
|
||||
|
||||
* All commands listed here may be used by server administrators in the admin room by sending them as messages.
|
||||
* If the `admin_escape_commands` configuration option is enabled, server administrators may run certain commands in public rooms by prefixing them with a single backslash. These commands will only run on _their_ homeserver, even if they are a member of another homeserver's admin room. Some sensitive commands cannot be used outside the admin room and will return an error.
|
||||
* All commands listed here may be used in the server's console, if it is enabled. Commands entered in the console do not require the `!admin` prefix.
|
||||
|
||||
## Categories
|
||||
|
||||
- [`!admin appservices`](appservices/): Commands for managing appservices
|
||||
- [`!admin users`](users/): Commands for managing local users
|
||||
- [`!admin token`](token/): Commands for managing registration tokens
|
||||
- [`!admin rooms`](rooms/): Commands for managing rooms
|
||||
- [`!admin federation`](federation/): Commands for managing federation
|
||||
- [`!admin server`](server/): Commands for managing the server
|
||||
- [`!admin media`](media/): Commands for managing media
|
||||
- [`!admin check`](check/): Commands for checking integrity
|
||||
- [`!admin debug`](debug/): Commands for debugging things
|
||||
- [`!admin query`](query/): Low-level queries for database getters and iterators
|
||||
38
docs/reference/admin/media.md
Normal file
38
docs/reference/admin/media.md
Normal file
@@ -0,0 +1,38 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin media`
|
||||
|
||||
Commands for managing media
|
||||
|
||||
|
||||
## `!admin media delete`
|
||||
|
||||
Deletes a single media file from our database and on the filesystem via a single MXC URL or event ID (not redacted)
|
||||
|
||||
## `!admin media delete-list`
|
||||
|
||||
Deletes a codeblock list of MXC URLs from our database and on the filesystem. This will always ignore errors
|
||||
|
||||
## `!admin media delete-past-remote-media`
|
||||
|
||||
Deletes all remote (and optionally local) media created before/after
|
||||
[duration] ago, using filesystem metadata first created at date, or
|
||||
fallback to last modified date. This will always ignore errors by
|
||||
default.
|
||||
|
||||
* Examples:
|
||||
* Delete all remote media older than a year:
|
||||
|
||||
`!admin media delete-past-remote-media -b 1y`
|
||||
|
||||
* Delete all remote and local media from 3 days ago, up until now:
|
||||
|
||||
`!admin media delete-past-remote-media -a 3d
|
||||
-yes-i-want-to-delete-local-media`
|
||||
|
||||
## `!admin media delete-all-from-user`
|
||||
|
||||
Deletes all the local media from a local user on our server. This will always ignore errors by default
|
||||
|
||||
## `!admin media delete-all-from-server`
|
||||
|
||||
Deletes all remote media from the specified remote server. This will always ignore errors by default
|
||||
194
docs/reference/admin/query.md
Normal file
194
docs/reference/admin/query.md
Normal file
@@ -0,0 +1,194 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin query`
|
||||
|
||||
Low-level queries for database getters and iterators
|
||||
|
||||
|
||||
## `!admin query account-data`
|
||||
|
||||
account_data.rs iterators and getters
|
||||
|
||||
### `!admin query account-data changes-since`
|
||||
|
||||
Returns all changes to the account data that happened after `since`
|
||||
|
||||
### `!admin query account-data account-data-get`
|
||||
|
||||
Searches the account data for a specific kind
|
||||
|
||||
## `!admin query appservice`
|
||||
|
||||
appservice.rs iterators and getters
|
||||
|
||||
### `!admin query appservice get-registration`
|
||||
|
||||
Gets the appservice registration info/details from the ID as a string
|
||||
|
||||
### `!admin query appservice all`
|
||||
|
||||
Gets all appservice registrations with their ID and registration info
|
||||
|
||||
## `!admin query presence`
|
||||
|
||||
presence.rs iterators and getters
|
||||
|
||||
### `!admin query presence get-presence`
|
||||
|
||||
Returns the latest presence event for the given user
|
||||
|
||||
### `!admin query presence presence-since`
|
||||
|
||||
Iterator of the most recent presence updates that happened after the event with id `since`
|
||||
|
||||
## `!admin query room-alias`
|
||||
|
||||
rooms/alias.rs iterators and getters
|
||||
|
||||
### `!admin query room-alias local-aliases-for-room`
|
||||
|
||||
Iterator of all our local room aliases for the room ID
|
||||
|
||||
### `!admin query room-alias all-local-aliases`
|
||||
|
||||
Iterator of all our local aliases in our database with their room IDs
|
||||
|
||||
## `!admin query room-state-cache`
|
||||
|
||||
rooms/state_cache iterators and getters
|
||||
|
||||
## `!admin query room-timeline`
|
||||
|
||||
rooms/timeline iterators and getters
|
||||
|
||||
## `!admin query globals`
|
||||
|
||||
globals.rs iterators and getters
|
||||
|
||||
### `!admin query globals signing-keys-for`
|
||||
|
||||
This returns an empty `Ok(BTreeMap<..>)` when there are no keys found for the server
|
||||
|
||||
## `!admin query sending`
|
||||
|
||||
sending.rs iterators and getters
|
||||
|
||||
### `!admin query sending active-requests`
|
||||
|
||||
Queries database for all `servercurrentevent_data`
|
||||
|
||||
### `!admin query sending active-requests-for`
|
||||
|
||||
Queries database for `servercurrentevent_data` but for a specific destination
|
||||
|
||||
This command takes only *one* format of these arguments:
|
||||
|
||||
appservice_id server_name user_id AND push_key
|
||||
|
||||
See src/service/sending/mod.rs for the definition of the `Destination` enum
|
||||
|
||||
### `!admin query sending queued-requests`
|
||||
|
||||
Queries database for `servernameevent_data` which are the queued up requests that will eventually be sent
|
||||
|
||||
This command takes only *one* format of these arguments:
|
||||
|
||||
appservice_id server_name user_id AND push_key
|
||||
|
||||
See src/service/sending/mod.rs for the definition of the `Destination` enum
|
||||
|
||||
## `!admin query users`
|
||||
|
||||
users.rs iterators and getters
|
||||
|
||||
## `!admin query resolver`
|
||||
|
||||
resolver service
|
||||
|
||||
### `!admin query resolver destinations-cache`
|
||||
|
||||
Query the destinations cache
|
||||
|
||||
### `!admin query resolver overrides-cache`
|
||||
|
||||
Query the overrides cache
|
||||
|
||||
### `!admin query resolver flush-cache`
|
||||
|
||||
Flush a given server from the resolver caches or flush them completely
|
||||
|
||||
* Examples:
|
||||
* Flush a specific server:
|
||||
|
||||
`!admin query resolver flush-cache matrix.example.com`
|
||||
|
||||
* Flush all resolver caches completely:
|
||||
|
||||
`!admin query resolver flush-cache --all`
|
||||
|
||||
## `!admin query pusher`
|
||||
|
||||
pusher service
|
||||
|
||||
### `!admin query pusher get-pushers`
|
||||
|
||||
Returns all the pushers for the user
|
||||
|
||||
## `!admin query short`
|
||||
|
||||
short service
|
||||
|
||||
## `!admin query raw`
|
||||
|
||||
raw service
|
||||
|
||||
### `!admin query raw raw-maps`
|
||||
|
||||
List database maps
|
||||
|
||||
### `!admin query raw raw-get`
|
||||
|
||||
Raw database query
|
||||
|
||||
### `!admin query raw raw-del`
|
||||
|
||||
Raw database delete (for string keys)
|
||||
|
||||
### `!admin query raw raw-keys`
|
||||
|
||||
Raw database keys iteration
|
||||
|
||||
### `!admin query raw raw-keys-sizes`
|
||||
|
||||
Raw database key size breakdown
|
||||
|
||||
### `!admin query raw raw-keys-total`
|
||||
|
||||
Raw database keys total bytes
|
||||
|
||||
### `!admin query raw raw-vals-sizes`
|
||||
|
||||
Raw database values size breakdown
|
||||
|
||||
### `!admin query raw raw-vals-total`
|
||||
|
||||
Raw database values total bytes
|
||||
|
||||
### `!admin query raw raw-iter`
|
||||
|
||||
Raw database items iteration
|
||||
|
||||
### `!admin query raw raw-keys-from`
|
||||
|
||||
Raw database keys iteration
|
||||
|
||||
### `!admin query raw raw-iter-from`
|
||||
|
||||
Raw database items iteration
|
||||
|
||||
### `!admin query raw raw-count`
|
||||
|
||||
Raw database record count
|
||||
|
||||
### `!admin query raw compact`
|
||||
|
||||
Compact database
|
||||
83
docs/reference/admin/rooms.md
Normal file
83
docs/reference/admin/rooms.md
Normal file
@@ -0,0 +1,83 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin rooms`
|
||||
|
||||
Commands for managing rooms
|
||||
|
||||
|
||||
## `!admin rooms list-rooms`
|
||||
|
||||
List all rooms the server knows about
|
||||
|
||||
## `!admin rooms info`
|
||||
|
||||
View information about a room we know about
|
||||
|
||||
### `!admin rooms info list-joined-members`
|
||||
|
||||
List joined members in a room
|
||||
|
||||
### `!admin rooms info view-room-topic`
|
||||
|
||||
Displays room topic
|
||||
|
||||
Room topics can be huge, so this is in its own separate command
|
||||
|
||||
## `!admin rooms moderation`
|
||||
|
||||
Manage moderation of remote or local rooms
|
||||
|
||||
### `!admin rooms moderation ban-room`
|
||||
|
||||
Bans a room from local users joining and evicts all our local users (including server admins) from the room. Also blocks any invites (local and remote) for the banned room, and disables federation entirely with it
|
||||
|
||||
### `!admin rooms moderation ban-list-of-rooms`
|
||||
|
||||
Bans a list of rooms (room IDs and room aliases) from a newline delimited codeblock similar to `user deactivate-all`. Applies the same steps as ban-room
|
||||
|
||||
### `!admin rooms moderation unban-room`
|
||||
|
||||
Unbans a room to allow local users to join again
|
||||
|
||||
### `!admin rooms moderation list-banned-rooms`
|
||||
|
||||
List of all rooms we have banned
|
||||
|
||||
## `!admin rooms alias`
|
||||
|
||||
Manage rooms' aliases
|
||||
|
||||
### `!admin rooms alias set`
|
||||
|
||||
Make an alias point to a room
|
||||
|
||||
### `!admin rooms alias remove`
|
||||
|
||||
Remove a local alias
|
||||
|
||||
### `!admin rooms alias which`
|
||||
|
||||
Show which room is using an alias
|
||||
|
||||
### `!admin rooms alias list`
|
||||
|
||||
List aliases currently being used
|
||||
|
||||
## `!admin rooms directory`
|
||||
|
||||
Manage the room directory
|
||||
|
||||
### `!admin rooms directory publish`
|
||||
|
||||
Publish a room to the room directory
|
||||
|
||||
### `!admin rooms directory unpublish`
|
||||
|
||||
Unpublish a room to the room directory
|
||||
|
||||
### `!admin rooms directory list`
|
||||
|
||||
List rooms that are published
|
||||
|
||||
## `!admin rooms exists`
|
||||
|
||||
Check if we know about a room
|
||||
49
docs/reference/admin/server.md
Normal file
49
docs/reference/admin/server.md
Normal file
@@ -0,0 +1,49 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin server`
|
||||
|
||||
Commands for managing the server
|
||||
|
||||
|
||||
## `!admin server uptime`
|
||||
|
||||
Time elapsed since startup
|
||||
|
||||
## `!admin server show-config`
|
||||
|
||||
Show configuration values
|
||||
|
||||
## `!admin server reload-config`
|
||||
|
||||
Reload configuration values
|
||||
|
||||
## `!admin server memory-usage`
|
||||
|
||||
Print database memory usage statistics
|
||||
|
||||
## `!admin server clear-caches`
|
||||
|
||||
Clears all of Continuwuity's caches
|
||||
|
||||
## `!admin server backup-database`
|
||||
|
||||
Performs an online backup of the database (only available for RocksDB at the moment)
|
||||
|
||||
## `!admin server list-backups`
|
||||
|
||||
List database backups
|
||||
|
||||
## `!admin server admin-notice`
|
||||
|
||||
Send a message to the admin room
|
||||
|
||||
## `!admin server reload-mods`
|
||||
|
||||
Hot-reload the server
|
||||
|
||||
## `!admin server restart`
|
||||
|
||||
Restart the server
|
||||
|
||||
## `!admin server shutdown`
|
||||
|
||||
Shutdown the server
|
||||
17
docs/reference/admin/token.md
Normal file
17
docs/reference/admin/token.md
Normal file
@@ -0,0 +1,17 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin token`
|
||||
|
||||
Commands for managing registration tokens
|
||||
|
||||
|
||||
## `!admin token issue`
|
||||
|
||||
Issue a new registration token
|
||||
|
||||
## `!admin token revoke`
|
||||
|
||||
Revoke a registration token
|
||||
|
||||
## `!admin token list`
|
||||
|
||||
List all registration tokens
|
||||
141
docs/reference/admin/users.md
Normal file
141
docs/reference/admin/users.md
Normal file
@@ -0,0 +1,141 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin users`
|
||||
|
||||
Commands for managing local users
|
||||
|
||||
|
||||
## `!admin users create-user`
|
||||
|
||||
Create a new user
|
||||
|
||||
## `!admin users reset-password`
|
||||
|
||||
Reset user password
|
||||
|
||||
## `!admin users deactivate`
|
||||
|
||||
Deactivate a user
|
||||
|
||||
User will be removed from all rooms by default. Use --no-leave-rooms to not leave all rooms by default.
|
||||
|
||||
## `!admin users deactivate-all`
|
||||
|
||||
Deactivate a list of users
|
||||
|
||||
Recommended to use in conjunction with list-local-users.
|
||||
|
||||
Users will be removed from joined rooms by default.
|
||||
|
||||
Can be overridden with --no-leave-rooms.
|
||||
|
||||
Removing a mass amount of users from a room may cause a significant amount of leave events. The time to leave rooms may depend significantly on joined rooms and servers.
|
||||
|
||||
This command needs a newline separated list of users provided in a Markdown code block below the command.
|
||||
|
||||
## `!admin users logout`
|
||||
|
||||
Forcefully log a user out of all of their devices.
|
||||
|
||||
This will invalidate all access tokens for the specified user, effectively logging them out from all sessions. Note that this is destructive and may result in data loss for the user, such as encryption keys. Use with caution. Can only be used in the admin room.
|
||||
|
||||
## `!admin users suspend`
|
||||
|
||||
Suspend a user
|
||||
|
||||
Suspended users are able to log in, sync, and read messages, but are not able to send events nor redact them, cannot change their profile, and are unable to join, invite to, or knock on rooms.
|
||||
|
||||
Suspended users can still leave rooms and deactivate their account. Suspending them effectively makes them read-only.
|
||||
|
||||
## `!admin users unsuspend`
|
||||
|
||||
Unsuspend a user
|
||||
|
||||
Reverses the effects of the `suspend` command, allowing the user to send messages, change their profile, create room invites, etc.
|
||||
|
||||
## `!admin users lock`
|
||||
|
||||
Lock a user
|
||||
|
||||
Locked users are unable to use their accounts beyond logging out. This is akin to a temporary deactivation that does not change the user's password. This can be used to quickly prevent a user from accessing their account.
|
||||
|
||||
## `!admin users unlock`
|
||||
|
||||
Unlock a user
|
||||
|
||||
Reverses the effects of the `lock` command, allowing the user to use their account again.
|
||||
|
||||
## `!admin users enable-login`
|
||||
|
||||
Enable login for a user
|
||||
|
||||
## `!admin users disable-login`
|
||||
|
||||
Disable login for a user
|
||||
|
||||
Disables login for the specified user without deactivating or locking their account. This prevents the user from obtaining new access tokens, but does not invalidate existing sessions.
|
||||
|
||||
## `!admin users list-users`
|
||||
|
||||
List local users in the database
|
||||
|
||||
## `!admin users list-joined-rooms`
|
||||
|
||||
Lists all the rooms (local and remote) that the specified user is joined in
|
||||
|
||||
## `!admin users force-join-room`
|
||||
|
||||
Manually join a local user to a room
|
||||
|
||||
## `!admin users force-leave-room`
|
||||
|
||||
Manually leave a local user from a room
|
||||
|
||||
## `!admin users force-leave-remote-room`
|
||||
|
||||
Manually leave a remote room for a local user
|
||||
|
||||
## `!admin users force-demote`
|
||||
|
||||
Forces the specified user to drop their power levels to the room default, if their permissions allow and the auth check permits
|
||||
|
||||
## `!admin users make-user-admin`
|
||||
|
||||
Grant server-admin privileges to a user
|
||||
|
||||
## `!admin users put-room-tag`
|
||||
|
||||
Puts a room tag for the specified user and room ID.
|
||||
|
||||
This is primarily useful if you'd like to set your admin room to the special "System Alerts" section in Element as a way to permanently see your admin room without it being buried away in your favourites or rooms. To do this, you would pass your user, your admin room's internal ID, and the tag name `m.server_notice`.
|
||||
|
||||
## `!admin users delete-room-tag`
|
||||
|
||||
Deletes the room tag for the specified user and room ID
|
||||
|
||||
## `!admin users get-room-tags`
|
||||
|
||||
Gets all the room tags for the specified user and room ID
|
||||
|
||||
## `!admin users redact-event`
|
||||
|
||||
Attempts to forcefully redact the specified event ID from the sender user
|
||||
|
||||
This is only valid for local users
|
||||
|
||||
## `!admin users force-join-list-of-local-users`
|
||||
|
||||
Force joins a specified list of local users to join the specified room.
|
||||
|
||||
Specify a codeblock of usernames.
|
||||
|
||||
At least 1 server admin must be in the room to reduce abuse.
|
||||
|
||||
Requires the `--yes-i-want-to-do-this` flag.
|
||||
|
||||
## `!admin users force-join-all-local-users`
|
||||
|
||||
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.
|
||||
@@ -1,21 +0,0 @@
|
||||
# Command-Line Help for `continuwuity`
|
||||
|
||||
This document contains the help content for the `continuwuity` command-line program.
|
||||
|
||||
**Command Overview:**
|
||||
|
||||
* [`continuwuity`↴](#continuwuity)
|
||||
|
||||
## `continuwuity`
|
||||
|
||||
A Matrix homeserver written in Rust, the official continuation of the conduwuit homeserver.
|
||||
|
||||
**Usage:** `continuwuity [OPTIONS]`
|
||||
|
||||
###### **Options:**
|
||||
|
||||
* `-c`, `--config <CONFIG>` — Path to the config TOML file (optional)
|
||||
* `-O`, `--option <OPTION>` — Override a configuration variable using TOML 'key=value' syntax
|
||||
* `--read-only` — Run in a stricter read-only --maintenance mode
|
||||
* `--maintenance` — Run in maintenance mode while refusing connections
|
||||
* `--execute <EXECUTE>` — Execute console command automatically after startup
|
||||
@@ -20,6 +20,16 @@ ### Lost access to admin room
|
||||
|
||||
## General potential issues
|
||||
|
||||
### Configuration not working as expected
|
||||
|
||||
Sometimes you can make a mistake in your configuration that
|
||||
means things don't get passed to Continuwuity correctly.
|
||||
This is particularly easy to do with environment variables.
|
||||
To check what configuration Continuwuity actually sees, you can
|
||||
use the `!admin server show-config` command in your admin room.
|
||||
Beware that this prints out any secrets in your configuration,
|
||||
so you might want to delete the result afterwards!
|
||||
|
||||
### Potential DNS issues when using Docker
|
||||
|
||||
Docker's DNS setup for containers in a non-default network intercepts queries to
|
||||
@@ -139,7 +149,7 @@ ### Database corruption
|
||||
|
||||
## Debugging
|
||||
|
||||
Note that users should not really be debugging things. If you find yourself
|
||||
Note that users should not really need to debug things. If you find yourself
|
||||
debugging and find the issue, please let us know and/or how we can fix it.
|
||||
Various debug commands can be found in `!admin debug`.
|
||||
|
||||
@@ -178,6 +188,31 @@ ### Pinging servers
|
||||
and simply fetches a string on a static JSON endpoint. It is very low cost both
|
||||
bandwidth and computationally.
|
||||
|
||||
### Enabling backtraces for errors
|
||||
|
||||
Continuwuity can capture backtraces (stack traces) for errors to help diagnose
|
||||
issues. Backtraces show the exact sequence of function calls that led to an
|
||||
error, which is invaluable for debugging.
|
||||
|
||||
To enable backtraces, set the `RUST_BACKTRACE` environment variable before starting Continuwuity:
|
||||
|
||||
```bash
|
||||
# For both panics and errors
|
||||
RUST_BACKTRACE=1 ./conduwuit
|
||||
|
||||
```
|
||||
|
||||
For systemd deployments, add this to your service file:
|
||||
|
||||
```ini
|
||||
[Service]
|
||||
Environment="RUST_BACKTRACE=1"
|
||||
```
|
||||
|
||||
Backtrace capture has a performance cost. Avoid leaving it on.
|
||||
You can also enable it only for panics by setting
|
||||
`RUST_BACKTRACE=1` and `RUST_LIB_BACKTRACE=0`.
|
||||
|
||||
### Allocator memory stats
|
||||
|
||||
When using jemalloc with jemallocator's `stats` feature (`--enable-stats`), you
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
# Setting up TURN/STURN
|
||||
|
||||
In order to make or receive calls, a TURN server is required. Continuwuity suggests
|
||||
using [Coturn](https://github.com/coturn/coturn) for this purpose, which is also
|
||||
available as a Docker image.
|
||||
|
||||
### Configuration
|
||||
|
||||
Create a configuration file called `coturn.conf` containing:
|
||||
|
||||
```
|
||||
use-auth-secret
|
||||
static-auth-secret=<a secret key>
|
||||
realm=<your server domain>
|
||||
```
|
||||
|
||||
A common way to generate a suitable alphanumeric secret key is by using `pwgen
|
||||
-s 64 1`.
|
||||
|
||||
These same values need to be set in Continuwuity. See the [example
|
||||
config](./reference/config.mdx) in the TURN section for configuring these and
|
||||
restart Continuwuity after.
|
||||
|
||||
`turn_secret` or a path to `turn_secret_file` must have a value of your
|
||||
coturn `static-auth-secret`, or use `turn_username` and `turn_password`
|
||||
if using legacy username:password TURN authentication (not preferred).
|
||||
|
||||
`turn_uris` must be the list of TURN URIs you would like to send to the client.
|
||||
Typically you will just replace the example domain `example.turn.uri` with the
|
||||
`realm` you set from the example config.
|
||||
|
||||
If you are using TURN over TLS, you can replace `turn:` with `turns:` in the
|
||||
`turn_uris` config option to instruct clients to attempt to connect to
|
||||
TURN over TLS. This is highly recommended.
|
||||
|
||||
If you need unauthenticated access to the TURN URIs, or some clients may be
|
||||
having trouble, you can enable `turn_guest_access` in Continuwuity which disables
|
||||
authentication for the TURN URI endpoint `/_matrix/client/v3/voip/turnServer`
|
||||
|
||||
### Run
|
||||
|
||||
Run the [Coturn](https://hub.docker.com/r/coturn/coturn) image using
|
||||
|
||||
```bash
|
||||
docker run -d --network=host -v
|
||||
$(pwd)/coturn.conf:/etc/coturn/turnserver.conf coturn/coturn
|
||||
```
|
||||
|
||||
or docker-compose. For the latter, paste the following section into a file
|
||||
called `docker-compose.yml` and run `docker compose up -d` in the same
|
||||
directory.
|
||||
|
||||
```yml
|
||||
version: 3
|
||||
services:
|
||||
turn:
|
||||
container_name: coturn-server
|
||||
image: docker.io/coturn/coturn
|
||||
restart: unless-stopped
|
||||
network_mode: "host"
|
||||
volumes:
|
||||
- ./coturn.conf:/etc/coturn/turnserver.conf
|
||||
```
|
||||
|
||||
To understand why the host networking mode is used and explore alternative
|
||||
configuration options, please visit [Coturn's Docker
|
||||
documentation](https://github.com/coturn/coturn/blob/master/docker/coturn/README.md).
|
||||
|
||||
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.example.com>/_matrix/client/r0/voip/turnServer" -H 'Authorization: Bearer <your_client_token>' | 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/)
|
||||
@@ -20,7 +20,7 @@ rec {
|
||||
# we need to keep the `web` directory which would be filtered out by the regular source filtering function
|
||||
#
|
||||
# https://crane.dev/API.html#cranelibcleancargosource
|
||||
isWebTemplate = path: _type: builtins.match ".*src/web.*" path != null;
|
||||
isWebTemplate = path: _type: builtins.match ".*(src/(web|service)|docs).*" path != null;
|
||||
isRust = craneLib.filterCargoSources;
|
||||
isNix = path: _type: builtins.match ".+/nix.*" path != null;
|
||||
webOrRustNotNix = p: t: !(isNix p t) && (isWebTemplate p t || isRust p t);
|
||||
|
||||
1878
package-lock.json
generated
1878
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -22,10 +22,9 @@
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"devDependencies": {
|
||||
"@rspress/core": "^2.0.0-rc.1",
|
||||
"@rspress/plugin-client-redirects": "^2.0.0-alpha.12",
|
||||
"@rspress/plugin-preview": "^2.0.0-beta.35",
|
||||
"@rspress/plugin-sitemap": "^2.0.0-beta.23",
|
||||
"@rspress/core": "^2.0.0",
|
||||
"@rspress/plugin-client-redirects": "^2.0.0",
|
||||
"@rspress/plugin-sitemap": "^2.0.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
tag-message = "chore: Release v{{version}}"
|
||||
tag-prefix = ""
|
||||
shared-version = true
|
||||
|
||||
publish = false
|
||||
|
||||
sign-commit = true
|
||||
sign-tag = true
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineConfig } from '@rspress/core';
|
||||
import { pluginPreview } from '@rspress/plugin-preview';
|
||||
import { pluginSitemap } from '@rspress/plugin-sitemap';
|
||||
import { pluginClientRedirects } from '@rspress/plugin-client-redirects';
|
||||
|
||||
@@ -41,7 +40,7 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
|
||||
plugins: [pluginPreview(), pluginSitemap({
|
||||
plugins: [pluginSitemap({
|
||||
siteUrl: 'https://continuwuity.org', // TODO: Set automatically in build pipeline
|
||||
}),
|
||||
pluginClientRedirects({
|
||||
@@ -54,6 +53,12 @@ export default defineConfig({
|
||||
}, {
|
||||
from: '/server_reference',
|
||||
to: '/reference/server'
|
||||
}, {
|
||||
from: '/community$',
|
||||
to: '/community/guidelines'
|
||||
}, {
|
||||
from: "^/turn",
|
||||
to: "/calls/turn",
|
||||
}
|
||||
]
|
||||
})],
|
||||
|
||||
@@ -87,7 +87,6 @@ serde-saphyr.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing.workspace = true
|
||||
ctor.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -19,43 +19,43 @@
|
||||
#[command(name = conduwuit_core::name(), version = conduwuit_core::version())]
|
||||
pub enum AdminCommand {
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing appservices
|
||||
/// Commands for managing appservices
|
||||
Appservices(AppserviceCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing local users
|
||||
/// Commands for managing local users
|
||||
Users(UserCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing registration tokens
|
||||
/// Commands for managing registration tokens
|
||||
Token(TokenCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing rooms
|
||||
/// Commands for managing rooms
|
||||
Rooms(RoomCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing federation
|
||||
/// Commands for managing federation
|
||||
Federation(FederationCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing the server
|
||||
/// Commands for managing the server
|
||||
Server(ServerCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing media
|
||||
/// Commands for managing media
|
||||
Media(MediaCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for checking integrity
|
||||
/// Commands for checking integrity
|
||||
Check(CheckCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for debugging things
|
||||
/// Commands for debugging things
|
||||
Debug(DebugCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Low-level queries for database getters and iterators
|
||||
/// Low-level queries for database getters and iterators
|
||||
Query(QueryCommand),
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[admin_command_dispatch]
|
||||
pub enum AppserviceCommand {
|
||||
/// - Register an appservice using its registration YAML
|
||||
/// Register an appservice using its registration YAML
|
||||
///
|
||||
/// This command needs a YAML generated by an appservice (such as a bridge),
|
||||
/// which must be provided in a Markdown code block below the command.
|
||||
@@ -17,7 +17,7 @@ pub enum AppserviceCommand {
|
||||
/// the old one.
|
||||
Register,
|
||||
|
||||
/// - Unregister an appservice using its ID
|
||||
/// Unregister an appservice using its ID
|
||||
///
|
||||
/// You can find the ID using the `list-appservices` command.
|
||||
Unregister {
|
||||
@@ -25,7 +25,7 @@ pub enum AppserviceCommand {
|
||||
appservice_identifier: String,
|
||||
},
|
||||
|
||||
/// - Show an appservice's config using its ID
|
||||
/// Show an appservice's config using its ID
|
||||
///
|
||||
/// You can find the ID using the `list-appservices` command.
|
||||
#[clap(alias("show"))]
|
||||
@@ -34,7 +34,7 @@ pub enum AppserviceCommand {
|
||||
appservice_identifier: String,
|
||||
},
|
||||
|
||||
/// - List all the currently registered appservices
|
||||
/// List all the currently registered appservices
|
||||
#[clap(alias("list"))]
|
||||
ListRegistered,
|
||||
}
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
|
||||
use crate::Context;
|
||||
|
||||
/// Uses the iterator in `src/database/key_value/users.rs` to iterator over
|
||||
/// every user in our database (remote and local). Reports total count, any
|
||||
/// errors if there were any, etc
|
||||
#[implement(Context, params = "<'_>")]
|
||||
pub(super) async fn check_all_users(&self) -> Result {
|
||||
let timer = tokio::time::Instant::now();
|
||||
|
||||
@@ -8,5 +8,8 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum CheckCommand {
|
||||
/// Uses the iterator in `src/database/key_value/users.rs` to iterator over
|
||||
/// every user in our database (remote and local). Reports total count, any
|
||||
/// errors if there were any, etc
|
||||
CheckAllUsers,
|
||||
}
|
||||
|
||||
@@ -819,32 +819,6 @@ pub(super) async fn time(&self) -> Result {
|
||||
self.write_str(&now).await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_dependencies(&self, names: bool) -> Result {
|
||||
if names {
|
||||
let out = info::cargo::dependencies_names().join(" ");
|
||||
return self.write_str(&out).await;
|
||||
}
|
||||
|
||||
let mut out = String::new();
|
||||
let deps = info::cargo::dependencies();
|
||||
writeln!(out, "| name | version | features |")?;
|
||||
writeln!(out, "| ---- | ------- | -------- |")?;
|
||||
for (name, dep) in deps {
|
||||
let version = dep.try_req().unwrap_or("*");
|
||||
let feats = dep.req_features();
|
||||
let feats = if !feats.is_empty() {
|
||||
feats.join(" ")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
writeln!(out, "| {name} | {version} | {feats} |")?;
|
||||
}
|
||||
|
||||
self.write_str(&out).await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn database_stats(
|
||||
&self,
|
||||
|
||||
@@ -12,18 +12,18 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum DebugCommand {
|
||||
/// - Echo input of admin command
|
||||
/// Echo input of admin command
|
||||
Echo {
|
||||
message: Vec<String>,
|
||||
},
|
||||
|
||||
/// - Get the auth_chain of a PDU
|
||||
/// Get the auth_chain of a PDU
|
||||
GetAuthChain {
|
||||
/// An event ID (the $ character followed by the base64 reference hash)
|
||||
event_id: OwnedEventId,
|
||||
},
|
||||
|
||||
/// - Parse and print a PDU from a JSON
|
||||
/// Parse and print a PDU from a JSON
|
||||
///
|
||||
/// The PDU event is only checked for validity and is not added to the
|
||||
/// database.
|
||||
@@ -32,13 +32,13 @@ pub enum DebugCommand {
|
||||
/// the command.
|
||||
ParsePdu,
|
||||
|
||||
/// - Retrieve and print a PDU by EventID from the Continuwuity database
|
||||
/// Retrieve and print a PDU by EventID from the Continuwuity database
|
||||
GetPdu {
|
||||
/// An event ID (a $ followed by the base64 reference hash)
|
||||
event_id: OwnedEventId,
|
||||
},
|
||||
|
||||
/// - Retrieve and print a PDU by PduId from the Continuwuity database
|
||||
/// Retrieve and print a PDU by PduId from the Continuwuity database
|
||||
GetShortPdu {
|
||||
/// Shortroomid integer
|
||||
shortroomid: ShortRoomId,
|
||||
@@ -47,7 +47,7 @@ pub enum DebugCommand {
|
||||
shorteventid: ShortEventId,
|
||||
},
|
||||
|
||||
/// - Attempts to retrieve a PDU from a remote server. **Does not** insert
|
||||
/// Attempts to retrieve a PDU from a remote server. **Does not** insert
|
||||
/// it into the database
|
||||
/// or persist it anywhere.
|
||||
GetRemotePdu {
|
||||
@@ -59,7 +59,7 @@ pub enum DebugCommand {
|
||||
server: OwnedServerName,
|
||||
},
|
||||
|
||||
/// - Same as `get-remote-pdu` but accepts a codeblock newline delimited
|
||||
/// Same as `get-remote-pdu` but accepts a codeblock newline delimited
|
||||
/// list of PDUs and a single server to fetch from
|
||||
GetRemotePduList {
|
||||
/// Argument for us to attempt to fetch all the events from the
|
||||
@@ -71,7 +71,7 @@ pub enum DebugCommand {
|
||||
force: bool,
|
||||
},
|
||||
|
||||
/// - Gets all the room state events for the specified room.
|
||||
/// Gets all the room state events for the specified room.
|
||||
///
|
||||
/// This is functionally equivalent to `GET
|
||||
/// /_matrix/client/v3/rooms/{roomid}/state`, except the admin command does
|
||||
@@ -86,7 +86,7 @@ pub enum DebugCommand {
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Get and display signing keys from local cache or remote server.
|
||||
/// Get and display signing keys from local cache or remote server.
|
||||
GetSigningKeys {
|
||||
server_name: Option<OwnedServerName>,
|
||||
|
||||
@@ -97,23 +97,23 @@ pub enum DebugCommand {
|
||||
query: bool,
|
||||
},
|
||||
|
||||
/// - Get and display signing keys from local cache or remote server.
|
||||
/// Get and display signing keys from local cache or remote server.
|
||||
GetVerifyKeys {
|
||||
server_name: Option<OwnedServerName>,
|
||||
},
|
||||
|
||||
/// - Sends a federation request to the remote server's
|
||||
/// Sends a federation request to the remote server's
|
||||
/// `/_matrix/federation/v1/version` endpoint and measures the latency it
|
||||
/// took for the server to respond
|
||||
Ping {
|
||||
server: OwnedServerName,
|
||||
},
|
||||
|
||||
/// - Forces device lists for all local and remote users to be updated (as
|
||||
/// Forces device lists for all local and remote users to be updated (as
|
||||
/// having new keys available)
|
||||
ForceDeviceListUpdates,
|
||||
|
||||
/// - Change tracing log level/filter on the fly
|
||||
/// Change tracing log level/filter on the fly
|
||||
///
|
||||
/// This accepts the same format as the `log` config option.
|
||||
ChangeLogLevel {
|
||||
@@ -125,34 +125,34 @@ pub enum DebugCommand {
|
||||
reset: bool,
|
||||
},
|
||||
|
||||
/// - Verify JSON signatures
|
||||
/// Verify JSON signatures
|
||||
///
|
||||
/// This command needs a JSON blob provided in a Markdown code block below
|
||||
/// the command.
|
||||
VerifyJson,
|
||||
|
||||
/// - Verify PDU
|
||||
/// Verify PDU
|
||||
///
|
||||
/// This re-verifies a PDU existing in the database found by ID.
|
||||
VerifyPdu {
|
||||
event_id: OwnedEventId,
|
||||
},
|
||||
|
||||
/// - Prints the very first PDU in the specified room (typically
|
||||
/// Prints the very first PDU in the specified room (typically
|
||||
/// m.room.create)
|
||||
FirstPduInRoom {
|
||||
/// The room ID
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Prints the latest ("last") PDU in the specified room (typically a
|
||||
/// Prints the latest ("last") PDU in the specified room (typically a
|
||||
/// message)
|
||||
LatestPduInRoom {
|
||||
/// The room ID
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Forcefully replaces the room state of our local copy of the specified
|
||||
/// Forcefully replaces the room state of our local copy of the specified
|
||||
/// room, with the copy (auth chain and room state events) the specified
|
||||
/// remote server says.
|
||||
///
|
||||
@@ -176,7 +176,7 @@ pub enum DebugCommand {
|
||||
event_id: Option<OwnedEventId>,
|
||||
},
|
||||
|
||||
/// - Runs a server name through Continuwuity's true destination resolution
|
||||
/// Runs a server name through Continuwuity's true destination resolution
|
||||
/// process
|
||||
///
|
||||
/// Useful for debugging well-known issues
|
||||
@@ -187,7 +187,7 @@ pub enum DebugCommand {
|
||||
no_cache: bool,
|
||||
},
|
||||
|
||||
/// - Print extended memory usage
|
||||
/// Print extended memory usage
|
||||
///
|
||||
/// Optional argument is a character mask (a sequence of characters in any
|
||||
/// order) which enable additional extended statistics. Known characters are
|
||||
@@ -196,23 +196,17 @@ pub enum DebugCommand {
|
||||
opts: Option<String>,
|
||||
},
|
||||
|
||||
/// - Print general tokio runtime metric totals.
|
||||
/// Print general tokio runtime metric totals.
|
||||
RuntimeMetrics,
|
||||
|
||||
/// - Print detailed tokio runtime metrics accumulated since last command
|
||||
/// Print detailed tokio runtime metrics accumulated since last command
|
||||
/// invocation.
|
||||
RuntimeInterval,
|
||||
|
||||
/// - Print the current time
|
||||
/// Print the current time
|
||||
Time,
|
||||
|
||||
/// - List dependencies
|
||||
ListDependencies {
|
||||
#[arg(short, long)]
|
||||
names: bool,
|
||||
},
|
||||
|
||||
/// - Get database statistics
|
||||
/// Get database statistics
|
||||
DatabaseStats {
|
||||
property: Option<String>,
|
||||
|
||||
@@ -220,10 +214,10 @@ pub enum DebugCommand {
|
||||
map: Option<String>,
|
||||
},
|
||||
|
||||
/// - Trim memory usage
|
||||
/// Trim memory usage
|
||||
TrimMemory,
|
||||
|
||||
/// - List database files
|
||||
/// List database files
|
||||
DatabaseFiles {
|
||||
map: Option<String>,
|
||||
|
||||
@@ -231,7 +225,7 @@ pub enum DebugCommand {
|
||||
level: Option<i32>,
|
||||
},
|
||||
|
||||
/// - Developer test stubs
|
||||
/// Developer test stubs
|
||||
#[command(subcommand)]
|
||||
#[allow(non_snake_case)]
|
||||
#[clap(hide(true))]
|
||||
|
||||
@@ -9,20 +9,20 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum FederationCommand {
|
||||
/// - List all rooms we are currently handling an incoming pdu from
|
||||
/// List all rooms we are currently handling an incoming pdu from
|
||||
IncomingFederation,
|
||||
|
||||
/// - Disables incoming federation handling for a room.
|
||||
/// Disables incoming federation handling for a room.
|
||||
DisableRoom {
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Enables incoming federation handling for a room again.
|
||||
/// Enables incoming federation handling for a room again.
|
||||
EnableRoom {
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Fetch `/.well-known/matrix/support` from the specified server
|
||||
/// Fetch `/.well-known/matrix/support` from the specified server
|
||||
///
|
||||
/// Despite the name, this is not a federation endpoint and does not go
|
||||
/// through the federation / server resolution process as per-spec this is
|
||||
@@ -35,7 +35,7 @@ pub enum FederationCommand {
|
||||
server_name: OwnedServerName,
|
||||
},
|
||||
|
||||
/// - Lists all the rooms we share/track with the specified *remote* user
|
||||
/// Lists all the rooms we share/track with the specified *remote* user
|
||||
RemoteUserInRooms {
|
||||
user_id: OwnedUserId,
|
||||
},
|
||||
|
||||
@@ -10,20 +10,20 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MediaCommand {
|
||||
/// - Deletes a single media file from our database and on the filesystem
|
||||
/// Deletes a single media file from our database and on the filesystem
|
||||
/// via a single MXC URL or event ID (not redacted)
|
||||
Delete {
|
||||
/// The MXC URL to delete
|
||||
#[arg(long)]
|
||||
mxc: Option<OwnedMxcUri>,
|
||||
|
||||
/// - The message event ID which contains the media and thumbnail MXC
|
||||
/// The message event ID which contains the media and thumbnail MXC
|
||||
/// URLs
|
||||
#[arg(long)]
|
||||
event_id: Option<OwnedEventId>,
|
||||
},
|
||||
|
||||
/// - Deletes a codeblock list of MXC URLs from our database and on the
|
||||
/// Deletes a codeblock list of MXC URLs from our database and on the
|
||||
/// filesystem. This will always ignore errors.
|
||||
DeleteList,
|
||||
|
||||
@@ -40,33 +40,33 @@ pub enum MediaCommand {
|
||||
/// * Delete all remote and local media from 3 days ago, up until now:
|
||||
///
|
||||
/// `!admin media delete-past-remote-media -a 3d
|
||||
/// --yes-i-want-to-delete-local-media`
|
||||
///-yes-i-want-to-delete-local-media`
|
||||
#[command(verbatim_doc_comment)]
|
||||
DeletePastRemoteMedia {
|
||||
/// - The relative time (e.g. 30s, 5m, 7d) from now within which to
|
||||
/// The relative time (e.g. 30s, 5m, 7d) from now within which to
|
||||
/// search
|
||||
duration: String,
|
||||
|
||||
/// - Only delete media created before [duration] ago
|
||||
/// Only delete media created before [duration] ago
|
||||
#[arg(long, short)]
|
||||
before: bool,
|
||||
|
||||
/// - Only delete media created after [duration] ago
|
||||
/// Only delete media created after [duration] ago
|
||||
#[arg(long, short)]
|
||||
after: bool,
|
||||
|
||||
/// - Long argument to additionally delete local media
|
||||
/// Long argument to additionally delete local media
|
||||
#[arg(long)]
|
||||
yes_i_want_to_delete_local_media: bool,
|
||||
},
|
||||
|
||||
/// - Deletes all the local media from a local user on our server. This will
|
||||
/// Deletes all the local media from a local user on our server. This will
|
||||
/// always ignore errors by default.
|
||||
DeleteAllFromUser {
|
||||
username: String,
|
||||
},
|
||||
|
||||
/// - Deletes all remote media from the specified remote server. This will
|
||||
/// Deletes all remote media from the specified remote server. This will
|
||||
/// always ignore errors by default.
|
||||
DeleteAllFromServer {
|
||||
server_name: OwnedServerName,
|
||||
|
||||
@@ -30,11 +30,8 @@
|
||||
|
||||
pub(crate) const PAGE_SIZE: usize = 100;
|
||||
|
||||
use ctor::{ctor, dtor};
|
||||
|
||||
conduwuit::mod_ctor! {}
|
||||
conduwuit::mod_dtor! {}
|
||||
conduwuit::rustc_flags_capture! {}
|
||||
|
||||
pub use crate::admin::AdminCommand;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/account_data.rs
|
||||
pub enum AccountDataCommand {
|
||||
/// - Returns all changes to the account data that happened after `since`.
|
||||
/// Returns all changes to the account data that happened after `since`.
|
||||
ChangesSince {
|
||||
/// Full user ID
|
||||
user_id: OwnedUserId,
|
||||
@@ -19,7 +19,7 @@ pub enum AccountDataCommand {
|
||||
room_id: Option<OwnedRoomId>,
|
||||
},
|
||||
|
||||
/// - Searches the account data for a specific kind.
|
||||
/// Searches the account data for a specific kind.
|
||||
AccountDataGet {
|
||||
/// Full user ID
|
||||
user_id: OwnedUserId,
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/appservice.rs
|
||||
pub enum AppserviceCommand {
|
||||
/// - Gets the appservice registration info/details from the ID as a string
|
||||
/// Gets the appservice registration info/details from the ID as a string
|
||||
GetRegistration {
|
||||
/// Appservice registration ID
|
||||
appservice_id: String,
|
||||
},
|
||||
|
||||
/// - Gets all appservice registrations with their ID and registration info
|
||||
/// Gets all appservice registrations with their ID and registration info
|
||||
All,
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ pub enum GlobalsCommand {
|
||||
|
||||
LastCheckForAnnouncementsId,
|
||||
|
||||
/// - This returns an empty `Ok(BTreeMap<..>)` when there are no keys found
|
||||
/// This returns an empty `Ok(BTreeMap<..>)` when there are no keys found
|
||||
/// for the server.
|
||||
SigningKeysFor {
|
||||
origin: OwnedServerName,
|
||||
|
||||
@@ -28,55 +28,55 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// Query tables from database
|
||||
pub enum QueryCommand {
|
||||
/// - account_data.rs iterators and getters
|
||||
/// account_data.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
AccountData(AccountDataCommand),
|
||||
|
||||
/// - appservice.rs iterators and getters
|
||||
/// appservice.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Appservice(AppserviceCommand),
|
||||
|
||||
/// - presence.rs iterators and getters
|
||||
/// presence.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Presence(PresenceCommand),
|
||||
|
||||
/// - rooms/alias.rs iterators and getters
|
||||
/// rooms/alias.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
RoomAlias(RoomAliasCommand),
|
||||
|
||||
/// - rooms/state_cache iterators and getters
|
||||
/// rooms/state_cache iterators and getters
|
||||
#[command(subcommand)]
|
||||
RoomStateCache(RoomStateCacheCommand),
|
||||
|
||||
/// - rooms/timeline iterators and getters
|
||||
/// rooms/timeline iterators and getters
|
||||
#[command(subcommand)]
|
||||
RoomTimeline(RoomTimelineCommand),
|
||||
|
||||
/// - globals.rs iterators and getters
|
||||
/// globals.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Globals(GlobalsCommand),
|
||||
|
||||
/// - sending.rs iterators and getters
|
||||
/// sending.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Sending(SendingCommand),
|
||||
|
||||
/// - users.rs iterators and getters
|
||||
/// users.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Users(UsersCommand),
|
||||
|
||||
/// - resolver service
|
||||
/// resolver service
|
||||
#[command(subcommand)]
|
||||
Resolver(ResolverCommand),
|
||||
|
||||
/// - pusher service
|
||||
/// pusher service
|
||||
#[command(subcommand)]
|
||||
Pusher(PusherCommand),
|
||||
|
||||
/// - short service
|
||||
/// short service
|
||||
#[command(subcommand)]
|
||||
Short(ShortCommand),
|
||||
|
||||
/// - raw service
|
||||
/// raw service
|
||||
#[command(subcommand)]
|
||||
Raw(RawCommand),
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/presence.rs
|
||||
pub enum PresenceCommand {
|
||||
/// - Returns the latest presence event for the given user.
|
||||
/// Returns the latest presence event for the given user.
|
||||
GetPresence {
|
||||
/// Full user ID
|
||||
user_id: OwnedUserId,
|
||||
},
|
||||
|
||||
/// - Iterator of the most recent presence updates that happened after the
|
||||
/// Iterator of the most recent presence updates that happened after the
|
||||
/// event with id `since`.
|
||||
PresenceSince {
|
||||
/// UNIX timestamp since (u64)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum PusherCommand {
|
||||
/// - Returns all the pushers for the user.
|
||||
/// Returns all the pushers for the user.
|
||||
GetPushers {
|
||||
/// Full user ID
|
||||
user_id: OwnedUserId,
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
/// Query tables from database
|
||||
pub enum RawCommand {
|
||||
/// - List database maps
|
||||
/// List database maps
|
||||
RawMaps,
|
||||
|
||||
/// - Raw database query
|
||||
/// Raw database query
|
||||
RawGet {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -32,7 +32,7 @@ pub enum RawCommand {
|
||||
key: String,
|
||||
},
|
||||
|
||||
/// - Raw database delete (for string keys)
|
||||
/// Raw database delete (for string keys)
|
||||
RawDel {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -41,7 +41,7 @@ pub enum RawCommand {
|
||||
key: String,
|
||||
},
|
||||
|
||||
/// - Raw database keys iteration
|
||||
/// Raw database keys iteration
|
||||
RawKeys {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -50,7 +50,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database key size breakdown
|
||||
/// Raw database key size breakdown
|
||||
RawKeysSizes {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
@@ -59,7 +59,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database keys total bytes
|
||||
/// Raw database keys total bytes
|
||||
RawKeysTotal {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
@@ -68,7 +68,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database values size breakdown
|
||||
/// Raw database values size breakdown
|
||||
RawValsSizes {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
@@ -77,7 +77,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database values total bytes
|
||||
/// Raw database values total bytes
|
||||
RawValsTotal {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
@@ -86,7 +86,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database items iteration
|
||||
/// Raw database items iteration
|
||||
RawIter {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -95,7 +95,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database keys iteration
|
||||
/// Raw database keys iteration
|
||||
RawKeysFrom {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -108,7 +108,7 @@ pub enum RawCommand {
|
||||
limit: Option<usize>,
|
||||
},
|
||||
|
||||
/// - Raw database items iteration
|
||||
/// Raw database items iteration
|
||||
RawIterFrom {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -121,7 +121,7 @@ pub enum RawCommand {
|
||||
limit: Option<usize>,
|
||||
},
|
||||
|
||||
/// - Raw database record count
|
||||
/// Raw database record count
|
||||
RawCount {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
@@ -130,7 +130,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Compact database
|
||||
/// Compact database
|
||||
Compact {
|
||||
#[arg(short, long, alias("column"))]
|
||||
map: Option<Vec<String>>,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use clap::Subcommand;
|
||||
use conduwuit::{Result, utils::time};
|
||||
use conduwuit::{Err, Result, utils::time};
|
||||
use futures::StreamExt;
|
||||
use ruma::OwnedServerName;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
/// Resolver service and caches
|
||||
pub enum ResolverCommand {
|
||||
/// Query the destinations cache
|
||||
@@ -18,6 +19,14 @@ pub enum ResolverCommand {
|
||||
OverridesCache {
|
||||
name: Option<String>,
|
||||
},
|
||||
|
||||
/// Flush a specific server from the resolver caches or everything
|
||||
FlushCache {
|
||||
name: Option<OwnedServerName>,
|
||||
|
||||
#[arg(short, long)]
|
||||
all: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
@@ -69,3 +78,18 @@ async fn overrides_cache(&self, server_name: Option<String>) -> Result {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
async fn flush_cache(&self, name: Option<OwnedServerName>, all: bool) -> Result {
|
||||
if all {
|
||||
self.services.resolver.cache.clear().await;
|
||||
writeln!(self, "Resolver caches cleared!").await
|
||||
} else if let Some(name) = name {
|
||||
self.services.resolver.cache.del_destination(&name);
|
||||
self.services.resolver.cache.del_override(&name);
|
||||
self.write_str(&format!("Cleared {name} from resolver caches!"))
|
||||
.await
|
||||
} else {
|
||||
Err!("Missing name. Supply a name or use --all to flush the whole cache.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,13 +13,13 @@ pub enum RoomAliasCommand {
|
||||
alias: OwnedRoomAliasId,
|
||||
},
|
||||
|
||||
/// - Iterator of all our local room aliases for the room ID
|
||||
/// Iterator of all our local room aliases for the room ID
|
||||
LocalAliasesForRoom {
|
||||
/// Full room ID
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Iterator of all our local aliases in our database with their room IDs
|
||||
/// Iterator of all our local aliases in our database with their room IDs
|
||||
AllLocalAliases,
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/sending.rs
|
||||
pub enum SendingCommand {
|
||||
/// - Queries database for all `servercurrentevent_data`
|
||||
/// Queries database for all `servercurrentevent_data`
|
||||
ActiveRequests,
|
||||
|
||||
/// - Queries database for `servercurrentevent_data` but for a specific
|
||||
/// Queries database for `servercurrentevent_data` but for a specific
|
||||
/// destination
|
||||
///
|
||||
/// This command takes only *one* format of these arguments:
|
||||
@@ -34,7 +34,7 @@ pub enum SendingCommand {
|
||||
push_key: Option<String>,
|
||||
},
|
||||
|
||||
/// - Queries database for `servernameevent_data` which are the queued up
|
||||
/// Queries database for `servernameevent_data` which are the queued up
|
||||
/// requests that will eventually be sent
|
||||
///
|
||||
/// This command takes only *one* format of these arguments:
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum RoomAliasCommand {
|
||||
/// - Make an alias point to a room.
|
||||
/// Make an alias point to a room.
|
||||
Set {
|
||||
#[arg(short, long)]
|
||||
/// Set the alias even if a room is already using it
|
||||
@@ -22,20 +22,20 @@ pub enum RoomAliasCommand {
|
||||
room_alias_localpart: String,
|
||||
},
|
||||
|
||||
/// - Remove a local alias
|
||||
/// Remove a local alias
|
||||
Remove {
|
||||
/// The alias localpart to remove (`alias`, not `#alias:servername.tld`)
|
||||
room_alias_localpart: String,
|
||||
},
|
||||
|
||||
/// - Show which room is using an alias
|
||||
/// Show which room is using an alias
|
||||
Which {
|
||||
/// The alias localpart to look up (`alias`, not
|
||||
/// `#alias:servername.tld`)
|
||||
room_alias_localpart: String,
|
||||
},
|
||||
|
||||
/// - List aliases currently being used
|
||||
/// List aliases currently being used
|
||||
List {
|
||||
/// If set, only list the aliases for this room
|
||||
room_id: Option<OwnedRoomId>,
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
|
||||
use crate::{PAGE_SIZE, admin_command, get_room_info};
|
||||
|
||||
#[allow(clippy::fn_params_excessive_bools)]
|
||||
#[admin_command]
|
||||
pub(super) async fn list_rooms(
|
||||
&self,
|
||||
page: Option<usize>,
|
||||
exclude_disabled: bool,
|
||||
exclude_banned: bool,
|
||||
include_empty: bool,
|
||||
no_details: bool,
|
||||
) -> Result {
|
||||
// TODO: i know there's a way to do this with clap, but i can't seem to find it
|
||||
@@ -28,6 +30,20 @@ pub(super) async fn list_rooms(
|
||||
.then_some(room_id)
|
||||
})
|
||||
.then(|room_id| get_room_info(self.services, room_id))
|
||||
.then(|(room_id, total_members, name)| async move {
|
||||
let local_members: Vec<_> = self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.active_local_users_in_room(&room_id)
|
||||
.collect()
|
||||
.await;
|
||||
let local_members = local_members.len();
|
||||
(room_id, total_members, local_members, name)
|
||||
})
|
||||
.filter_map(|(room_id, total_members, local_members, name)| async move {
|
||||
(include_empty || local_members > 0).then_some((room_id, total_members, name))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
|
||||
|
||||
@@ -7,19 +7,19 @@
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum RoomDirectoryCommand {
|
||||
/// - Publish a room to the room directory
|
||||
/// Publish a room to the room directory
|
||||
Publish {
|
||||
/// The room id of the room to publish
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Unpublish a room to the room directory
|
||||
/// Unpublish a room to the room directory
|
||||
Unpublish {
|
||||
/// The room id of the room to unpublish
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - List rooms that are published
|
||||
/// List rooms that are published
|
||||
List {
|
||||
page: Option<usize>,
|
||||
},
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum RoomInfoCommand {
|
||||
/// - List joined members in a room
|
||||
/// List joined members in a room
|
||||
ListJoinedMembers {
|
||||
room_id: OwnedRoomId,
|
||||
|
||||
@@ -17,7 +17,7 @@ pub enum RoomInfoCommand {
|
||||
local_only: bool,
|
||||
},
|
||||
|
||||
/// - Displays room topic
|
||||
/// Displays room topic
|
||||
///
|
||||
/// Room topics can be huge, so this is in its
|
||||
/// own separate command
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum RoomCommand {
|
||||
/// - List all rooms the server knows about
|
||||
/// List all rooms the server knows about
|
||||
#[clap(alias = "list")]
|
||||
ListRooms {
|
||||
page: Option<usize>,
|
||||
@@ -30,6 +30,10 @@ pub enum RoomCommand {
|
||||
#[arg(long)]
|
||||
exclude_banned: bool,
|
||||
|
||||
/// Includes disconnected/empty rooms (rooms with zero members)
|
||||
#[arg(long)]
|
||||
include_empty: bool,
|
||||
|
||||
#[arg(long)]
|
||||
/// Whether to only output room IDs without supplementary room
|
||||
/// information
|
||||
@@ -37,22 +41,22 @@ pub enum RoomCommand {
|
||||
},
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - View information about a room we know about
|
||||
/// View information about a room we know about
|
||||
Info(RoomInfoCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Manage moderation of remote or local rooms
|
||||
/// Manage moderation of remote or local rooms
|
||||
Moderation(RoomModerationCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Manage rooms' aliases
|
||||
/// Manage rooms' aliases
|
||||
Alias(RoomAliasCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Manage the room directory
|
||||
/// Manage the room directory
|
||||
Directory(RoomDirectoryCommand),
|
||||
|
||||
/// - Check if we know about a room
|
||||
/// Check if we know about a room
|
||||
Exists {
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum RoomModerationCommand {
|
||||
/// - Bans a room from local users joining and evicts all our local users
|
||||
/// Bans a room from local users joining and evicts all our local users
|
||||
/// (including server
|
||||
/// admins)
|
||||
/// from the room. Also blocks any invites (local and remote) for the
|
||||
@@ -24,19 +24,19 @@ pub enum RoomModerationCommand {
|
||||
room: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Bans a list of rooms (room IDs and room aliases) from a newline
|
||||
/// Bans a list of rooms (room IDs and room aliases) from a newline
|
||||
/// delimited codeblock similar to `user deactivate-all`. Applies the same
|
||||
/// steps as ban-room
|
||||
BanListOfRooms,
|
||||
|
||||
/// - Unbans a room to allow local users to join again
|
||||
/// Unbans a room to allow local users to join again
|
||||
UnbanRoom {
|
||||
/// The room in the format of `!roomid:example.com` or a room alias in
|
||||
/// the format of `#roomalias:example.com`
|
||||
room: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - List of all rooms we have banned
|
||||
/// List of all rooms we have banned
|
||||
ListBannedRooms {
|
||||
#[arg(long)]
|
||||
/// Whether to only output room IDs without supplementary room
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{fmt::Write, path::PathBuf, sync::Arc};
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use conduwuit::{
|
||||
Err, Result, info,
|
||||
Err, Result,
|
||||
utils::{stream::IterStream, time},
|
||||
warn,
|
||||
};
|
||||
@@ -59,34 +59,6 @@ pub(super) async fn reload_config(&self, path: Option<PathBuf>) -> Result {
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_features(&self, available: bool, enabled: bool, comma: bool) -> Result {
|
||||
let delim = if comma { "," } else { " " };
|
||||
if enabled && !available {
|
||||
let features = info::rustc::features().join(delim);
|
||||
let out = format!("`\n{features}\n`");
|
||||
return self.write_str(&out).await;
|
||||
}
|
||||
|
||||
if available && !enabled {
|
||||
let features = info::cargo::features().join(delim);
|
||||
let out = format!("`\n{features}\n`");
|
||||
return self.write_str(&out).await;
|
||||
}
|
||||
|
||||
let mut features = String::new();
|
||||
let enabled = info::rustc::features();
|
||||
let available = info::cargo::features();
|
||||
for feature in available {
|
||||
let active = enabled.contains(&feature.as_str());
|
||||
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 memory_usage(&self) -> Result {
|
||||
let services_usage = self.services.memory_usage().await?;
|
||||
|
||||
@@ -10,58 +10,46 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum ServerCommand {
|
||||
/// - Time elapsed since startup
|
||||
/// Time elapsed since startup
|
||||
Uptime,
|
||||
|
||||
/// - Show configuration values
|
||||
/// Show configuration values
|
||||
ShowConfig,
|
||||
|
||||
/// - Reload configuration values
|
||||
/// Reload configuration values
|
||||
ReloadConfig {
|
||||
path: Option<PathBuf>,
|
||||
},
|
||||
|
||||
/// - List the features built into the server
|
||||
ListFeatures {
|
||||
#[arg(short, long)]
|
||||
available: bool,
|
||||
|
||||
#[arg(short, long)]
|
||||
enabled: bool,
|
||||
|
||||
#[arg(short, long)]
|
||||
comma: bool,
|
||||
},
|
||||
|
||||
/// - Print database memory usage statistics
|
||||
/// Print database memory usage statistics
|
||||
MemoryUsage,
|
||||
|
||||
/// - Clears all of Continuwuity's caches
|
||||
/// Clears all of Continuwuity's caches
|
||||
ClearCaches,
|
||||
|
||||
/// - Performs an online backup of the database (only available for RocksDB
|
||||
/// Performs an online backup of the database (only available for RocksDB
|
||||
/// at the moment)
|
||||
BackupDatabase,
|
||||
|
||||
/// - List database backups
|
||||
/// List database backups
|
||||
ListBackups,
|
||||
|
||||
/// - Send a message to the admin room.
|
||||
/// Send a message to the admin room.
|
||||
AdminNotice {
|
||||
message: Vec<String>,
|
||||
},
|
||||
|
||||
/// - Hot-reload the server
|
||||
/// Hot-reload the server
|
||||
#[clap(alias = "reload")]
|
||||
ReloadMods,
|
||||
|
||||
#[cfg(unix)]
|
||||
/// - Restart the server
|
||||
/// Restart the server
|
||||
Restart {
|
||||
#[arg(short, long)]
|
||||
force: bool,
|
||||
},
|
||||
|
||||
/// - Shutdown the server
|
||||
/// Shutdown the server
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum TokenCommand {
|
||||
/// - Issue a new registration token
|
||||
/// Issue a new registration token
|
||||
#[clap(name = "issue")]
|
||||
IssueToken {
|
||||
/// When this token will expire.
|
||||
@@ -16,14 +16,14 @@ pub enum TokenCommand {
|
||||
expires: TokenExpires,
|
||||
},
|
||||
|
||||
/// - Revoke a registration token
|
||||
/// Revoke a registration token
|
||||
#[clap(name = "revoke")]
|
||||
RevokeToken {
|
||||
/// The token to revoke.
|
||||
token: String,
|
||||
},
|
||||
|
||||
/// - List all registration tokens
|
||||
/// List all registration tokens
|
||||
#[clap(name = "list")]
|
||||
ListTokens,
|
||||
}
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
fmt::Write as _,
|
||||
};
|
||||
|
||||
use api::client::{
|
||||
full_user_deactivate, join_room_by_id_helper, leave_all_rooms, leave_room, remote_leave_room,
|
||||
update_avatar_url, update_displayname,
|
||||
};
|
||||
use api::client::{full_user_deactivate, join_room_by_id_helper, leave_room, remote_leave_room};
|
||||
use conduwuit::{
|
||||
Err, Result, debug, debug_warn, error, info, is_equal_to,
|
||||
Err, Result, debug_warn, error, info,
|
||||
matrix::{Event, pdu::PduBuilder},
|
||||
utils::{self, ReadyExt},
|
||||
warn,
|
||||
@@ -143,7 +140,6 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
|
||||
self.services.globals.server_name().to_owned(),
|
||||
room_server_name.to_owned(),
|
||||
],
|
||||
None,
|
||||
&None,
|
||||
)
|
||||
.await
|
||||
@@ -171,27 +167,8 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
|
||||
|
||||
// we dont add a device since we're not the user, just the creator
|
||||
|
||||
// if this account creation is from the CLI / --execute, invite the first user
|
||||
// to admin room
|
||||
if let Ok(admin_room) = self.services.admin.get_admin_room().await {
|
||||
if self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(&admin_room)
|
||||
.await
|
||||
.is_ok_and(is_equal_to!(1))
|
||||
{
|
||||
self.services
|
||||
.admin
|
||||
.make_user_admin(&user_id)
|
||||
.boxed()
|
||||
.await?;
|
||||
warn!("Granting {user_id} admin privileges as the first user");
|
||||
}
|
||||
} else {
|
||||
debug!("create_user admin command called without an admin room being available");
|
||||
}
|
||||
// Make the first user to register an administrator and disable first-run mode.
|
||||
self.services.firstrun.empower_first_user(&user_id).await?;
|
||||
|
||||
self.write_str(&format!("Created user with user_id: {user_id} and password: `{password}`"))
|
||||
.await
|
||||
@@ -227,9 +204,6 @@ pub(super) async fn deactivate(&self, no_leave_rooms: bool, user_id: String) ->
|
||||
full_user_deactivate(self.services, &user_id, &all_joined_rooms)
|
||||
.boxed()
|
||||
.await?;
|
||||
update_displayname(self.services, &user_id, None, &all_joined_rooms).await;
|
||||
update_avatar_url(self.services, &user_id, None, None, &all_joined_rooms).await;
|
||||
leave_all_rooms(self.services, &user_id).await;
|
||||
}
|
||||
|
||||
self.write_str(&format!("User {user_id} has been deactivated"))
|
||||
@@ -406,10 +380,6 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
|
||||
full_user_deactivate(self.services, &user_id, &all_joined_rooms)
|
||||
.boxed()
|
||||
.await?;
|
||||
update_displayname(self.services, &user_id, None, &all_joined_rooms).await;
|
||||
update_avatar_url(self.services, &user_id, None, None, &all_joined_rooms)
|
||||
.await;
|
||||
leave_all_rooms(self.services, &user_id).await;
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -559,7 +529,6 @@ pub(super) async fn force_join_list_of_local_users(
|
||||
&room_id,
|
||||
Some(String::from(BULK_JOIN_REASON)),
|
||||
&servers,
|
||||
None,
|
||||
&None,
|
||||
)
|
||||
.await
|
||||
@@ -645,7 +614,6 @@ pub(super) async fn force_join_all_local_users(
|
||||
&room_id,
|
||||
Some(String::from(BULK_JOIN_REASON)),
|
||||
&servers,
|
||||
None,
|
||||
&None,
|
||||
)
|
||||
.await
|
||||
@@ -685,8 +653,7 @@ pub(super) async fn force_join_room(
|
||||
self.services.globals.user_is_local(&user_id),
|
||||
"Parsed user_id must be a local user"
|
||||
);
|
||||
join_room_by_id_helper(self.services, &user_id, &room_id, None, &servers, None, &None)
|
||||
.await?;
|
||||
join_room_by_id_helper(self.services, &user_id, &room_id, None, &servers, &None).await?;
|
||||
|
||||
self.write_str(&format!("{user_id} has been joined to {room_id}.",))
|
||||
.await
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum UserCommand {
|
||||
/// - Create a new user
|
||||
/// Create a new user
|
||||
#[clap(alias = "create")]
|
||||
CreateUser {
|
||||
/// Username of the new user
|
||||
@@ -18,7 +18,7 @@ pub enum UserCommand {
|
||||
password: Option<String>,
|
||||
},
|
||||
|
||||
/// - Reset user password
|
||||
/// Reset user password
|
||||
ResetPassword {
|
||||
/// Log out existing sessions
|
||||
#[arg(short, long)]
|
||||
@@ -29,7 +29,7 @@ pub enum UserCommand {
|
||||
password: Option<String>,
|
||||
},
|
||||
|
||||
/// - Deactivate a user
|
||||
/// Deactivate a user
|
||||
///
|
||||
/// User will be removed from all rooms by default.
|
||||
/// Use --no-leave-rooms to not leave all rooms by default.
|
||||
@@ -39,7 +39,7 @@ pub enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Deactivate a list of users
|
||||
/// Deactivate a list of users
|
||||
///
|
||||
/// Recommended to use in conjunction with list-local-users.
|
||||
///
|
||||
@@ -62,7 +62,7 @@ pub enum UserCommand {
|
||||
force: bool,
|
||||
},
|
||||
|
||||
/// - Forcefully log a user out of all of their devices.
|
||||
/// Forcefully log a user out of all of their devices.
|
||||
///
|
||||
/// This will invalidate all access tokens for the specified user,
|
||||
/// effectively logging them out from all sessions.
|
||||
@@ -74,7 +74,7 @@ pub enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Suspend a user
|
||||
/// Suspend a user
|
||||
///
|
||||
/// Suspended users are able to log in, sync, and read messages, but are not
|
||||
/// able to send events nor redact them, cannot change their profile, and
|
||||
@@ -87,7 +87,7 @@ pub enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Unsuspend a user
|
||||
/// Unsuspend a user
|
||||
///
|
||||
/// Reverses the effects of the `suspend` command, allowing the user to send
|
||||
/// messages, change their profile, create room invites, etc.
|
||||
@@ -96,7 +96,7 @@ pub enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Lock a user
|
||||
/// Lock a user
|
||||
///
|
||||
/// Locked users are unable to use their accounts beyond logging out. This
|
||||
/// is akin to a temporary deactivation that does not change the user's
|
||||
@@ -107,7 +107,7 @@ pub enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Unlock a user
|
||||
/// Unlock a user
|
||||
///
|
||||
/// Reverses the effects of the `lock` command, allowing the user to use
|
||||
/// their account again.
|
||||
@@ -116,13 +116,13 @@ pub enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Enable login for a user
|
||||
/// Enable login for a user
|
||||
EnableLogin {
|
||||
/// Username of the user to enable login for
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Disable login for a user
|
||||
/// Disable login for a user
|
||||
///
|
||||
/// Disables login for the specified user without deactivating or locking
|
||||
/// their account. This prevents the user from obtaining new access tokens,
|
||||
@@ -132,48 +132,48 @@ pub enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - List local users in the database
|
||||
/// List local users in the database
|
||||
#[clap(alias = "list")]
|
||||
ListUsers,
|
||||
|
||||
/// - Lists all the rooms (local and remote) that the specified user is
|
||||
/// Lists all the rooms (local and remote) that the specified user is
|
||||
/// joined in
|
||||
ListJoinedRooms {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Manually join a local user to a room.
|
||||
/// Manually join a local user to a room.
|
||||
ForceJoinRoom {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Manually leave a local user from a room.
|
||||
/// Manually leave a local user from a room.
|
||||
ForceLeaveRoom {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Manually leave a remote room for a local user.
|
||||
/// Manually leave a remote room for a local user.
|
||||
ForceLeaveRemoteRoom {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
via: Option<String>,
|
||||
},
|
||||
|
||||
/// - Forces the specified user to drop their power levels to the room
|
||||
/// Forces the specified user to drop their power levels to the room
|
||||
/// default, if their permissions allow and the auth check permits
|
||||
ForceDemote {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Grant server-admin privileges to a user.
|
||||
/// Grant server-admin privileges to a user.
|
||||
MakeUserAdmin {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Puts a room tag for the specified user and room ID.
|
||||
/// Puts a room tag for the specified user and room ID.
|
||||
///
|
||||
/// This is primarily useful if you'd like to set your admin room
|
||||
/// to the special "System Alerts" section in Element as a way to
|
||||
@@ -186,20 +186,20 @@ pub enum UserCommand {
|
||||
tag: String,
|
||||
},
|
||||
|
||||
/// - Deletes the room tag for the specified user and room ID
|
||||
/// Deletes the room tag for the specified user and room ID
|
||||
DeleteRoomTag {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomId,
|
||||
tag: String,
|
||||
},
|
||||
|
||||
/// - Gets all the room tags for the specified user and room ID
|
||||
/// Gets all the room tags for the specified user and room ID
|
||||
GetRoomTags {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Attempts to forcefully redact the specified event ID from the sender
|
||||
/// Attempts to forcefully redact the specified event ID from the sender
|
||||
/// user
|
||||
///
|
||||
/// This is only valid for local users
|
||||
@@ -207,7 +207,7 @@ pub enum UserCommand {
|
||||
event_id: OwnedEventId,
|
||||
},
|
||||
|
||||
/// - Force joins a specified list of local users to join the specified
|
||||
/// Force joins a specified list of local users to join the specified
|
||||
/// room.
|
||||
///
|
||||
/// Specify a codeblock of usernames.
|
||||
@@ -222,7 +222,7 @@ pub enum UserCommand {
|
||||
yes_i_want_to_do_this: bool,
|
||||
},
|
||||
|
||||
/// - Force joins all local users to the specified room.
|
||||
/// Force joins all local users to the specified room.
|
||||
///
|
||||
/// At least 1 server admin must be in the room to reduce abuse.
|
||||
///
|
||||
|
||||
@@ -28,6 +28,10 @@ gzip_compression = [
|
||||
"conduwuit-service/gzip_compression",
|
||||
"reqwest/gzip",
|
||||
]
|
||||
http3 = [
|
||||
"conduwuit-core/http3",
|
||||
"conduwuit-service/http3",
|
||||
]
|
||||
io_uring = [
|
||||
"conduwuit-service/io_uring",
|
||||
]
|
||||
@@ -91,7 +95,6 @@ serde.workspace = true
|
||||
sha1.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
ctor.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
1
src/api/admin/mod.rs
Normal file
1
src/api/admin/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod rooms;
|
||||
132
src/api/admin/rooms/ban.rs
Normal file
132
src/api/admin/rooms/ban.rs
Normal file
@@ -0,0 +1,132 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, info, utils::ReadyExt, warn};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
OwnedRoomAliasId, continuwuity_admin_api::rooms,
|
||||
events::room::message::RoomMessageEventContent,
|
||||
};
|
||||
|
||||
use crate::{Ruma, client::leave_room};
|
||||
|
||||
/// # `PUT /_continuwuity/admin/rooms/{roomID}/ban`
|
||||
///
|
||||
/// Bans or unbans a room.
|
||||
pub(crate) async fn ban_room(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<rooms::ban::v1::Request>,
|
||||
) -> Result<rooms::ban::v1::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
if !services.users.is_admin(sender_user).await {
|
||||
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||
}
|
||||
|
||||
if body.banned {
|
||||
// Don't ban again if already banned
|
||||
if services.rooms.metadata.is_banned(&body.room_id).await {
|
||||
return Err!(Request(InvalidParam("Room is already banned")));
|
||||
}
|
||||
info!(%sender_user, "Banning room {}", body.room_id);
|
||||
|
||||
services
|
||||
.admin
|
||||
.notice(&format!("{sender_user} banned {} (ban in progress)", body.room_id))
|
||||
.await;
|
||||
|
||||
let mut users = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&body.room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.ready_filter(|user| services.globals.user_is_local(user))
|
||||
.boxed();
|
||||
let mut evicted = Vec::new();
|
||||
let mut failed_evicted = Vec::new();
|
||||
|
||||
while let Some(ref user_id) = users.next().await {
|
||||
info!("Evicting user {} from room {}", user_id, body.room_id);
|
||||
match leave_room(&services, user_id, &body.room_id, None)
|
||||
.boxed()
|
||||
.await
|
||||
{
|
||||
| Ok(()) => {
|
||||
services.rooms.state_cache.forget(&body.room_id, user_id);
|
||||
evicted.push(user_id.clone());
|
||||
},
|
||||
| Err(e) => {
|
||||
warn!("Failed to evict user {} from room {}: {}", user_id, body.room_id, e);
|
||||
failed_evicted.push(user_id.clone());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let aliases: Vec<OwnedRoomAliasId> = services
|
||||
.rooms
|
||||
.alias
|
||||
.local_aliases_for_room(&body.room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
for alias in &aliases {
|
||||
info!("Removing alias {} for banned room {}", alias, body.room_id);
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.remove_alias(alias, &services.globals.server_user)
|
||||
.await?;
|
||||
}
|
||||
|
||||
services.rooms.directory.set_not_public(&body.room_id); // remove from the room directory
|
||||
services.rooms.metadata.ban_room(&body.room_id, true); // prevent further joins
|
||||
services.rooms.metadata.disable_room(&body.room_id, true); // disable federation
|
||||
|
||||
services
|
||||
.admin
|
||||
.notice(&format!(
|
||||
"Finished banning {}: Removed {} users ({} failed) and {} aliases",
|
||||
body.room_id,
|
||||
evicted.len(),
|
||||
failed_evicted.len(),
|
||||
aliases.len()
|
||||
))
|
||||
.await;
|
||||
if !evicted.is_empty() || !failed_evicted.is_empty() || !aliases.is_empty() {
|
||||
let msg = services
|
||||
.admin
|
||||
.text_or_file(RoomMessageEventContent::text_markdown(format!(
|
||||
"Removed users:\n{}\n\nFailed to remove users:\n{}\n\nRemoved aliases: {}",
|
||||
evicted
|
||||
.iter()
|
||||
.map(|u| u.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
failed_evicted
|
||||
.iter()
|
||||
.map(|u| u.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
aliases
|
||||
.iter()
|
||||
.map(|a| a.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
)))
|
||||
.await;
|
||||
services.admin.send_message(msg).await.ok();
|
||||
}
|
||||
|
||||
Ok(rooms::ban::v1::Response::new(evicted, failed_evicted, aliases))
|
||||
} else {
|
||||
// Don't unban if not banned
|
||||
if !services.rooms.metadata.is_banned(&body.room_id).await {
|
||||
return Err!(Request(InvalidParam("Room is not banned")));
|
||||
}
|
||||
info!(%sender_user, "Unbanning room {}", body.room_id);
|
||||
services.rooms.metadata.disable_room(&body.room_id, false);
|
||||
services.rooms.metadata.ban_room(&body.room_id, false);
|
||||
services
|
||||
.admin
|
||||
.notice(&format!("{sender_user} unbanned {}", body.room_id))
|
||||
.await;
|
||||
Ok(rooms::ban::v1::Response::new(Vec::new(), Vec::new(), Vec::new()))
|
||||
}
|
||||
}
|
||||
35
src/api/admin/rooms/list.rs
Normal file
35
src/api/admin/rooms/list.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result};
|
||||
use futures::StreamExt;
|
||||
use ruma::{OwnedRoomId, continuwuity_admin_api::rooms};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `GET /_continuwuity/admin/rooms/list`
|
||||
///
|
||||
/// Lists all rooms known to this server, excluding banned ones.
|
||||
pub(crate) async fn list_rooms(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<rooms::list::v1::Request>,
|
||||
) -> Result<rooms::list::v1::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
if !services.users.is_admin(sender_user).await {
|
||||
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||
}
|
||||
|
||||
let mut rooms: Vec<OwnedRoomId> = services
|
||||
.rooms
|
||||
.metadata
|
||||
.iter_ids()
|
||||
.filter_map(|room_id| async move {
|
||||
if !services.rooms.metadata.is_banned(room_id).await {
|
||||
Some(room_id.to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
.await;
|
||||
rooms.sort();
|
||||
Ok(rooms::list::v1::Response::new(rooms))
|
||||
}
|
||||
2
src/api/admin/rooms/mod.rs
Normal file
2
src/api/admin/rooms/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod ban;
|
||||
pub mod list;
|
||||
@@ -3,7 +3,7 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{
|
||||
Err, Error, Event, Result, debug_info, err, error, info, is_equal_to,
|
||||
Err, Error, Event, Result, debug_info, err, error, info,
|
||||
matrix::pdu::PduBuilder,
|
||||
utils::{self, ReadyExt, stream::BroadbandExt},
|
||||
warn,
|
||||
@@ -26,6 +26,7 @@
|
||||
events::{
|
||||
GlobalAccountDataEventType, StateEventType,
|
||||
room::{
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
message::RoomMessageEventContent,
|
||||
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||
},
|
||||
@@ -147,7 +148,12 @@ pub(crate) async fn register_route(
|
||||
let is_guest = body.kind == RegistrationKind::Guest;
|
||||
let emergency_mode_enabled = services.config.emergency_password.is_some();
|
||||
|
||||
if !services.config.allow_registration && body.appservice_info.is_none() {
|
||||
// Allow registration if it's enabled in the config file or if this is the first
|
||||
// run (so the first user account can be created)
|
||||
let allow_registration =
|
||||
services.config.allow_registration || services.firstrun.is_first_run();
|
||||
|
||||
if !allow_registration && body.appservice_info.is_none() {
|
||||
match (body.username.as_ref(), body.initial_device_display_name.as_ref()) {
|
||||
| (Some(username), Some(device_display_name)) => {
|
||||
info!(
|
||||
@@ -184,17 +190,10 @@ pub(crate) async fn register_route(
|
||||
)));
|
||||
}
|
||||
|
||||
if is_guest
|
||||
&& (!services.config.allow_guest_registration
|
||||
|| (services.config.allow_registration
|
||||
&& services
|
||||
.registration_tokens
|
||||
.get_config_file_token()
|
||||
.is_some()))
|
||||
{
|
||||
if is_guest && !services.config.allow_guest_registration {
|
||||
info!(
|
||||
"Guest registration disabled / registration enabled with token configured, \
|
||||
rejecting guest registration attempt, initial device name: \"{}\"",
|
||||
"Guest registration disabled, rejecting guest registration attempt, initial device \
|
||||
name: \"{}\"",
|
||||
body.initial_device_display_name.as_deref().unwrap_or("")
|
||||
);
|
||||
return Err!(Request(GuestAccessForbidden("Guest registration is disabled.")));
|
||||
@@ -308,54 +307,63 @@ pub(crate) async fn register_route(
|
||||
let skip_auth = body.appservice_info.is_some() || is_guest;
|
||||
|
||||
// Populate required UIAA flows
|
||||
if services
|
||||
.registration_tokens
|
||||
.iterate_tokens()
|
||||
.next()
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
// Registration token required
|
||||
|
||||
if services.firstrun.is_first_run() {
|
||||
// Registration token forced while in first-run mode
|
||||
uiaainfo.flows.push(AuthFlow {
|
||||
stages: vec![AuthType::RegistrationToken],
|
||||
});
|
||||
}
|
||||
if services.config.recaptcha_private_site_key.is_some() {
|
||||
if let Some(pubkey) = &services.config.recaptcha_site_key {
|
||||
// ReCaptcha required
|
||||
uiaainfo
|
||||
.flows
|
||||
.push(AuthFlow { stages: vec![AuthType::ReCaptcha] });
|
||||
uiaainfo.params = serde_json::value::to_raw_value(&serde_json::json!({
|
||||
"m.login.recaptcha": {
|
||||
"public_key": pubkey,
|
||||
},
|
||||
}))
|
||||
.expect("Failed to serialize recaptcha params");
|
||||
}
|
||||
}
|
||||
|
||||
if uiaainfo.flows.is_empty() && !skip_auth {
|
||||
// Registration isn't _disabled_, but there's no captcha configured and no
|
||||
// registration tokens currently set. Bail out by default unless open
|
||||
// registration was explicitly enabled.
|
||||
if !services
|
||||
.config
|
||||
.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
|
||||
} else {
|
||||
if services
|
||||
.registration_tokens
|
||||
.iterate_tokens()
|
||||
.next()
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
return Err!(Request(Forbidden(
|
||||
"This server is not accepting registrations at this time."
|
||||
)));
|
||||
// Registration token required
|
||||
uiaainfo.flows.push(AuthFlow {
|
||||
stages: vec![AuthType::RegistrationToken],
|
||||
});
|
||||
}
|
||||
|
||||
// We have open registration enabled (😧), provide a dummy stage
|
||||
uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow { stages: vec![AuthType::Dummy] }],
|
||||
completed: Vec::new(),
|
||||
params: Box::default(),
|
||||
session: None,
|
||||
auth_error: None,
|
||||
};
|
||||
if services.config.recaptcha_private_site_key.is_some() {
|
||||
if let Some(pubkey) = &services.config.recaptcha_site_key {
|
||||
// ReCaptcha required
|
||||
uiaainfo
|
||||
.flows
|
||||
.push(AuthFlow { stages: vec![AuthType::ReCaptcha] });
|
||||
uiaainfo.params = serde_json::value::to_raw_value(&serde_json::json!({
|
||||
"m.login.recaptcha": {
|
||||
"public_key": pubkey,
|
||||
},
|
||||
}))
|
||||
.expect("Failed to serialize recaptcha params");
|
||||
}
|
||||
}
|
||||
|
||||
if uiaainfo.flows.is_empty() && !skip_auth {
|
||||
// Registration isn't _disabled_, but there's no captcha configured and no
|
||||
// registration tokens currently set. Bail out by default unless open
|
||||
// registration was explicitly enabled.
|
||||
if !services
|
||||
.config
|
||||
.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
|
||||
{
|
||||
return Err!(Request(Forbidden(
|
||||
"This server is not accepting registrations at this time."
|
||||
)));
|
||||
}
|
||||
|
||||
// We have open registration enabled (😧), provide a dummy stage
|
||||
uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow { stages: vec![AuthType::Dummy] }],
|
||||
completed: Vec::new(),
|
||||
params: Box::default(),
|
||||
session: None,
|
||||
auth_error: None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if !skip_auth {
|
||||
@@ -513,39 +521,29 @@ pub(crate) async fn register_route(
|
||||
}
|
||||
}
|
||||
|
||||
// If this is the first real user, grant them admin privileges except for guest
|
||||
// users
|
||||
// Note: the server user is generated first
|
||||
if !is_guest {
|
||||
if let Ok(admin_room) = services.admin.get_admin_room().await {
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(&admin_room)
|
||||
.await
|
||||
.is_ok_and(is_equal_to!(1))
|
||||
{
|
||||
services.admin.make_user_admin(&user_id).boxed().await?;
|
||||
warn!("Granting {user_id} admin privileges as the first user");
|
||||
} else if services.config.suspend_on_register {
|
||||
// This is not an admin, suspend them.
|
||||
// Note that we can still do auto joins for suspended users
|
||||
// Make the first user to register an administrator and disable first-run mode.
|
||||
let was_first_user = services.firstrun.empower_first_user(&user_id).await?;
|
||||
|
||||
// If the registering user was not the first and we're suspending users on
|
||||
// register, suspend them.
|
||||
if !was_first_user && services.config.suspend_on_register {
|
||||
// Note that we can still do auto joins for suspended users
|
||||
services
|
||||
.users
|
||||
.suspend_account(&user_id, &services.globals.server_user)
|
||||
.await;
|
||||
// And send an @room notice to the admin room, to prompt admins to review the
|
||||
// new user and ideally unsuspend them if deemed appropriate.
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.users
|
||||
.suspend_account(&user_id, &services.globals.server_user)
|
||||
.await;
|
||||
// And send an @room notice to the admin room, to prompt admins to review the
|
||||
// new user and ideally unsuspend them if deemed appropriate.
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_loud_message(RoomMessageEventContent::text_plain(format!(
|
||||
"User {user_id} has been suspended as they are not the first user \
|
||||
on this server. Please review and unsuspend them if appropriate."
|
||||
)))
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
.admin
|
||||
.send_loud_message(RoomMessageEventContent::text_plain(format!(
|
||||
"User {user_id} has been suspended as they are not the first user on \
|
||||
this server. Please review and unsuspend them if appropriate."
|
||||
)))
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -582,7 +580,6 @@ pub(crate) async fn register_route(
|
||||
&room_id,
|
||||
Some("Automatically joining this room upon registration".to_owned()),
|
||||
&[services.globals.server_name().to_owned(), room_server_name.to_owned()],
|
||||
None,
|
||||
&body.appservice_info,
|
||||
)
|
||||
.boxed()
|
||||
@@ -815,9 +812,6 @@ pub(crate) async fn deactivate_route(
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
super::update_displayname(&services, sender_user, None, &all_joined_rooms).await;
|
||||
super::update_avatar_url(&services, sender_user, None, None, &all_joined_rooms).await;
|
||||
|
||||
full_user_deactivate(&services, sender_user, &all_joined_rooms)
|
||||
.boxed()
|
||||
.await?;
|
||||
@@ -907,9 +901,6 @@ pub async fn full_user_deactivate(
|
||||
) -> Result<()> {
|
||||
services.users.deactivate_account(user_id).await.ok();
|
||||
|
||||
super::update_displayname(services, user_id, None, all_joined_rooms).await;
|
||||
super::update_avatar_url(services, user_id, None, None, all_joined_rooms).await;
|
||||
|
||||
services
|
||||
.users
|
||||
.all_profile_keys(user_id)
|
||||
@@ -918,9 +909,11 @@ pub async fn full_user_deactivate(
|
||||
})
|
||||
.await;
|
||||
|
||||
for room_id in all_joined_rooms {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
// TODO: Rescind all user invites
|
||||
|
||||
let mut pdu_queue: Vec<(PduBuilder, &OwnedRoomId)> = Vec::new();
|
||||
|
||||
for room_id in all_joined_rooms {
|
||||
let room_power_levels = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
@@ -948,30 +941,33 @@ pub async fn full_user_deactivate(
|
||||
if user_can_demote_self {
|
||||
let mut power_levels_content = room_power_levels.unwrap_or_default();
|
||||
power_levels_content.users.remove(user_id);
|
||||
|
||||
// ignore errors so deactivation doesn't fail
|
||||
match services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &power_levels_content),
|
||||
user_id,
|
||||
Some(room_id),
|
||||
&state_lock,
|
||||
)
|
||||
.await
|
||||
{
|
||||
| Err(e) => {
|
||||
warn!(%room_id, %user_id, "Failed to demote user's own power level: {e}");
|
||||
},
|
||||
| _ => {
|
||||
info!("Demoted {user_id} in {room_id} as part of account deactivation");
|
||||
},
|
||||
}
|
||||
let pl_evt = PduBuilder::state(String::new(), &power_levels_content);
|
||||
pdu_queue.push((pl_evt, room_id));
|
||||
}
|
||||
|
||||
// Leave the room
|
||||
pdu_queue.push((
|
||||
PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
|
||||
avatar_url: None,
|
||||
blurhash: None,
|
||||
membership: MembershipState::Leave,
|
||||
displayname: None,
|
||||
join_authorized_via_users_server: None,
|
||||
reason: None,
|
||||
is_direct: None,
|
||||
third_party_invite: None,
|
||||
redact_events: None,
|
||||
}),
|
||||
room_id,
|
||||
));
|
||||
|
||||
// TODO: Redact all messages sent by the user in the room
|
||||
}
|
||||
|
||||
super::leave_all_rooms(services, user_id).boxed().await;
|
||||
super::update_all_rooms(services, pdu_queue, user_id).await;
|
||||
for room_id in all_joined_rooms {
|
||||
services.rooms.state_cache.forget(room_id, user_id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
use crate::{
|
||||
Ruma,
|
||||
client::message::{event_filter, ignored_filter, lazy_loading_witness, visibility_filter},
|
||||
client::{
|
||||
is_ignored_pdu,
|
||||
message::{event_filter, ignored_filter, lazy_loading_witness, visibility_filter},
|
||||
},
|
||||
};
|
||||
|
||||
const LIMIT_MAX: usize = 100;
|
||||
@@ -78,6 +81,9 @@ pub(crate) async fn get_context_route(
|
||||
return Err!(Request(NotFound("Event not found.")));
|
||||
}
|
||||
|
||||
// Return M_SENDER_IGNORED if the sender of base_event is ignored (MSC4406)
|
||||
is_ignored_pdu(&services, &base_pdu, sender_user).await?;
|
||||
|
||||
let base_count = base_id.pdu_count();
|
||||
|
||||
let base_event = ignored_filter(&services, (base_count, base_pdu), sender_user);
|
||||
|
||||
@@ -91,8 +91,11 @@ pub(crate) async fn upload_keys_route(
|
||||
.users
|
||||
.get_device_keys(sender_user, sender_device)
|
||||
.await
|
||||
.and_then(|keys| keys.deserialize().map_err(Into::into))
|
||||
{
|
||||
if existing_keys.json().get() == device_keys.json().get() {
|
||||
// NOTE: also serves as a workaround for a nheko bug which omits cross-signing
|
||||
// NOTE: signatures when re-uploading the same DeviceKeys.
|
||||
if existing_keys.keys == deser_device_keys.keys {
|
||||
debug!(
|
||||
%sender_user,
|
||||
%sender_device,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use ruma::{
|
||||
OwnedServerName, RoomId, UserId,
|
||||
RoomId, UserId,
|
||||
api::{client::membership::invite_user, federation::membership::create_invite},
|
||||
events::{
|
||||
invite_permission_config::FilterLevel,
|
||||
@@ -203,19 +203,10 @@ pub(crate) async fn invite_helper(
|
||||
))));
|
||||
}
|
||||
|
||||
let origin: OwnedServerName = serde_json::from_value(serde_json::to_value(
|
||||
value
|
||||
.get("origin")
|
||||
.ok_or_else(|| err!(Request(BadJson("Event missing origin field."))))?,
|
||||
)?)
|
||||
.map_err(|e| {
|
||||
err!(Request(BadJson(warn!("Origin field in event is not a valid server name: {e}"))))
|
||||
})?;
|
||||
|
||||
let pdu_id = services
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&origin, room_id, &event_id, value, true)
|
||||
.handle_incoming_pdu(recipient_user.server_name(), room_id, &event_id, value, true)
|
||||
.boxed()
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, debug, debug_info, debug_warn, err, error, info,
|
||||
Err, Result, debug, debug_info, debug_warn, err, error, info, is_true,
|
||||
matrix::{
|
||||
StateKey,
|
||||
event::{gen_event_id, gen_event_id_canonical_json},
|
||||
@@ -15,6 +15,7 @@
|
||||
utils::{
|
||||
self, shuffle,
|
||||
stream::{IterStream, ReadyExt},
|
||||
to_canonical_object,
|
||||
},
|
||||
warn,
|
||||
};
|
||||
@@ -25,7 +26,7 @@
|
||||
api::{
|
||||
client::{
|
||||
error::ErrorKind,
|
||||
membership::{ThirdPartySigned, join_room_by_id, join_room_by_id_or_alias},
|
||||
membership::{join_room_by_id, join_room_by_id_or_alias},
|
||||
},
|
||||
federation::{self},
|
||||
},
|
||||
@@ -33,7 +34,7 @@
|
||||
events::{
|
||||
StateEventType,
|
||||
room::{
|
||||
join_rules::{AllowRule, JoinRule},
|
||||
join_rules::JoinRule,
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
},
|
||||
@@ -47,9 +48,13 @@
|
||||
timeline::pdu_fits,
|
||||
},
|
||||
};
|
||||
use tokio::join;
|
||||
|
||||
use super::{banned_room_check, validate_remote_member_event_stub};
|
||||
use crate::Ruma;
|
||||
use crate::{
|
||||
Ruma,
|
||||
server::{select_authorising_user, user_can_perform_restricted_join},
|
||||
};
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/join`
|
||||
///
|
||||
@@ -115,7 +120,6 @@ pub(crate) async fn join_room_by_id_route(
|
||||
&body.room_id,
|
||||
body.reason.clone(),
|
||||
&servers,
|
||||
body.third_party_signed.as_ref(),
|
||||
&body.appservice_info,
|
||||
)
|
||||
.boxed()
|
||||
@@ -247,7 +251,6 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
&room_id,
|
||||
body.reason.clone(),
|
||||
&servers,
|
||||
body.third_party_signed.as_ref(),
|
||||
appservice_info,
|
||||
)
|
||||
.boxed()
|
||||
@@ -262,7 +265,6 @@ pub async fn join_room_by_id_helper(
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
servers: &[OwnedServerName],
|
||||
third_party_signed: Option<&ThirdPartySigned>,
|
||||
appservice_info: &Option<RegistrationInfo>,
|
||||
) -> Result<join_room_by_id::v3::Response> {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
@@ -350,17 +352,9 @@ pub async fn join_room_by_id_helper(
|
||||
}
|
||||
|
||||
if server_in_room {
|
||||
join_room_by_id_helper_local(
|
||||
services,
|
||||
sender_user,
|
||||
room_id,
|
||||
reason,
|
||||
servers,
|
||||
third_party_signed,
|
||||
state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
join_room_by_id_helper_local(services, sender_user, room_id, reason, servers, state_lock)
|
||||
.boxed()
|
||||
.await?;
|
||||
} else {
|
||||
// Ask a remote server if we are not participating in this room
|
||||
join_room_by_id_helper_remote(
|
||||
@@ -369,7 +363,6 @@ pub async fn join_room_by_id_helper(
|
||||
room_id,
|
||||
reason,
|
||||
servers,
|
||||
third_party_signed,
|
||||
state_lock,
|
||||
)
|
||||
.boxed()
|
||||
@@ -385,7 +378,6 @@ async fn join_room_by_id_helper_remote(
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
servers: &[OwnedServerName],
|
||||
_third_party_signed: Option<&ThirdPartySigned>,
|
||||
state_lock: RoomMutexGuard,
|
||||
) -> Result {
|
||||
info!("Joining {room_id} over federation.");
|
||||
@@ -395,11 +387,10 @@ async fn join_room_by_id_helper_remote(
|
||||
|
||||
info!("make_join finished");
|
||||
|
||||
let Some(room_version_id) = make_join_response.room_version else {
|
||||
return Err!(BadServerResponse("Remote room version is not supported by conduwuit"));
|
||||
};
|
||||
let room_version_id = make_join_response.room_version.unwrap_or(RoomVersionId::V1);
|
||||
|
||||
if !services.server.supported_room_version(&room_version_id) {
|
||||
// How did we get here?
|
||||
return Err!(BadServerResponse(
|
||||
"Remote room version {room_version_id} is not supported by conduwuit"
|
||||
));
|
||||
@@ -428,10 +419,6 @@ async fn join_room_by_id_helper_remote(
|
||||
}
|
||||
};
|
||||
|
||||
join_event_stub.insert(
|
||||
"origin".to_owned(),
|
||||
CanonicalJsonValue::String(services.globals.server_name().as_str().to_owned()),
|
||||
);
|
||||
join_event_stub.insert(
|
||||
"origin_server_ts".to_owned(),
|
||||
CanonicalJsonValue::Integer(
|
||||
@@ -743,87 +730,45 @@ async fn join_room_by_id_helper_local(
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
servers: &[OwnedServerName],
|
||||
_third_party_signed: Option<&ThirdPartySigned>,
|
||||
state_lock: RoomMutexGuard,
|
||||
) -> Result {
|
||||
debug_info!("We can join locally");
|
||||
let join_rules = services.rooms.state_accessor.get_join_rules(room_id).await;
|
||||
info!("Joining room locally");
|
||||
|
||||
let mut restricted_join_authorized = None;
|
||||
match join_rules {
|
||||
| JoinRule::Restricted(restricted) | JoinRule::KnockRestricted(restricted) => {
|
||||
for restriction in restricted.allow {
|
||||
match restriction {
|
||||
| AllowRule::RoomMembership(membership) => {
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender_user, &membership.room_id)
|
||||
.await
|
||||
{
|
||||
restricted_join_authorized = Some(true);
|
||||
break;
|
||||
}
|
||||
},
|
||||
| AllowRule::UnstableSpamChecker => {
|
||||
match services
|
||||
.antispam
|
||||
.meowlnir_accept_make_join(room_id.to_owned(), sender_user.to_owned())
|
||||
.await
|
||||
{
|
||||
| Ok(()) => {
|
||||
restricted_join_authorized = Some(true);
|
||||
break;
|
||||
},
|
||||
| Err(_) =>
|
||||
return Err!(Request(Forbidden(
|
||||
"Antispam rejected join request."
|
||||
))),
|
||||
}
|
||||
},
|
||||
| _ => {},
|
||||
}
|
||||
let (room_version, join_rules, is_invited) = join!(
|
||||
services.rooms.state.get_room_version(room_id),
|
||||
services.rooms.state_accessor.get_join_rules(room_id),
|
||||
services.rooms.state_cache.is_invited(sender_user, room_id)
|
||||
);
|
||||
|
||||
let room_version = room_version?;
|
||||
let mut auth_user: Option<OwnedUserId> = None;
|
||||
if !is_invited && matches!(join_rules, JoinRule::Restricted(_) | JoinRule::KnockRestricted(_))
|
||||
{
|
||||
use RoomVersionId::*;
|
||||
if !matches!(room_version, V1 | V2 | V3 | V4 | V5 | V6 | V7) {
|
||||
// This is a restricted room, check if we can complete the join requirements
|
||||
// locally.
|
||||
let needs_auth_user =
|
||||
user_can_perform_restricted_join(services, sender_user, room_id, &room_version)
|
||||
.await;
|
||||
if needs_auth_user.is_ok_and(is_true!()) {
|
||||
// If there was an error or the value is false, we'll try joining over
|
||||
// federation. Since it's Ok(true), we can authorise this locally.
|
||||
// If we can't select a local user, this will remain None, the join will fail,
|
||||
// and we'll fall back to federation.
|
||||
auth_user = select_authorising_user(services, room_id, sender_user, &state_lock)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
},
|
||||
| _ => {},
|
||||
}
|
||||
let join_authorized_via_users_server = if restricted_join_authorized.is_none() {
|
||||
None
|
||||
} else {
|
||||
match restricted_join_authorized.unwrap() {
|
||||
| true => services
|
||||
.rooms
|
||||
.state_cache
|
||||
.local_users_in_room(room_id)
|
||||
.filter(|user| {
|
||||
trace!("Checking if {user} can invite {sender_user} to {room_id}");
|
||||
services.rooms.state_accessor.user_can_invite(
|
||||
room_id,
|
||||
user,
|
||||
sender_user,
|
||||
&state_lock,
|
||||
)
|
||||
})
|
||||
.boxed()
|
||||
.next()
|
||||
.await
|
||||
.map(ToOwned::to_owned),
|
||||
| false => {
|
||||
warn!(
|
||||
"Join authorization failed for restricted join in room {room_id} for user \
|
||||
{sender_user}"
|
||||
);
|
||||
return Err!(Request(Forbidden("You are not authorized to join this room.")));
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let content = RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason: reason.clone(),
|
||||
join_authorized_via_users_server,
|
||||
join_authorized_via_users_server: auth_user,
|
||||
..RoomMemberEventContent::new(MembershipState::Join)
|
||||
};
|
||||
|
||||
@@ -839,6 +784,7 @@ async fn join_room_by_id_helper_local(
|
||||
)
|
||||
.await
|
||||
else {
|
||||
info!("Joined room locally");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
@@ -846,138 +792,13 @@ async fn join_room_by_id_helper_local(
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
warn!(
|
||||
info!(
|
||||
?error,
|
||||
servers = %servers.len(),
|
||||
"Could not join restricted room locally, attempting remote join",
|
||||
remote_servers = %servers.len(),
|
||||
"Could not join room locally, attempting remote join",
|
||||
);
|
||||
let Ok((make_join_response, remote_server)) =
|
||||
make_join_request(services, sender_user, room_id, servers).await
|
||||
else {
|
||||
return Err(error);
|
||||
};
|
||||
|
||||
let Some(room_version_id) = make_join_response.room_version else {
|
||||
return Err!(BadServerResponse("Remote room version is not supported by conduwuit"));
|
||||
};
|
||||
|
||||
if !services.server.supported_room_version(&room_version_id) {
|
||||
return Err!(BadServerResponse(
|
||||
"Remote room version {room_version_id} is not supported by conduwuit"
|
||||
));
|
||||
}
|
||||
|
||||
let mut join_event_stub: CanonicalJsonObject =
|
||||
serde_json::from_str(make_join_response.event.get()).map_err(|e| {
|
||||
err!(BadServerResponse("Invalid make_join event json received from server: {e:?}"))
|
||||
})?;
|
||||
|
||||
validate_remote_member_event_stub(
|
||||
&MembershipState::Join,
|
||||
sender_user,
|
||||
room_id,
|
||||
&join_event_stub,
|
||||
)?;
|
||||
|
||||
let join_authorized_via_users_server = join_event_stub
|
||||
.get("content")
|
||||
.map(|s| {
|
||||
s.as_object()?
|
||||
.get("join_authorised_via_users_server")?
|
||||
.as_str()
|
||||
})
|
||||
.and_then(|s| OwnedUserId::try_from(s.unwrap_or_default()).ok());
|
||||
|
||||
join_event_stub.insert(
|
||||
"origin".to_owned(),
|
||||
CanonicalJsonValue::String(services.globals.server_name().as_str().to_owned()),
|
||||
);
|
||||
join_event_stub.insert(
|
||||
"origin_server_ts".to_owned(),
|
||||
CanonicalJsonValue::Integer(
|
||||
utils::millis_since_unix_epoch()
|
||||
.try_into()
|
||||
.expect("Timestamp is valid js_int value"),
|
||||
),
|
||||
);
|
||||
join_event_stub.insert(
|
||||
"content".to_owned(),
|
||||
to_canonical_value(RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason,
|
||||
join_authorized_via_users_server,
|
||||
..RoomMemberEventContent::new(MembershipState::Join)
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
);
|
||||
|
||||
// We keep the "event_id" in the pdu only in v1 or
|
||||
// v2 rooms
|
||||
match room_version_id {
|
||||
| RoomVersionId::V1 | RoomVersionId::V2 => {},
|
||||
| _ => {
|
||||
join_event_stub.remove("event_id");
|
||||
},
|
||||
}
|
||||
|
||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
||||
// to be present
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut join_event_stub, &room_version_id)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&join_event_stub, &room_version_id)?;
|
||||
|
||||
// Add event_id back
|
||||
join_event_stub
|
||||
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
|
||||
|
||||
// It has enough fields to be called a proper event now
|
||||
let join_event = join_event_stub;
|
||||
|
||||
let send_join_response = services
|
||||
.sending
|
||||
.send_synapse_request(
|
||||
&remote_server,
|
||||
federation::membership::create_join_event::v2::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: event_id.clone(),
|
||||
omit_members: false,
|
||||
pdu: services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(join_event.clone())
|
||||
.await,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(signed_raw) = send_join_response.room_state.event {
|
||||
let (signed_event_id, signed_value) =
|
||||
gen_event_id_canonical_json(&signed_raw, &room_version_id).map_err(|e| {
|
||||
err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}"))))
|
||||
})?;
|
||||
|
||||
if signed_event_id != event_id {
|
||||
return Err!(Request(BadJson(
|
||||
warn!(%signed_event_id, %event_id, "Server {remote_server} sent event with wrong event ID")
|
||||
)));
|
||||
}
|
||||
|
||||
drop(state_lock);
|
||||
services
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&remote_server, room_id, &signed_event_id, signed_value, true)
|
||||
.boxed()
|
||||
.await?;
|
||||
} else {
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
join_room_by_id_helper_remote(services, sender_user, room_id, reason, servers, state_lock)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn make_join_request(
|
||||
@@ -986,17 +807,16 @@ async fn make_join_request(
|
||||
room_id: &RoomId,
|
||||
servers: &[OwnedServerName],
|
||||
) -> Result<(federation::membership::prepare_join_event::v1::Response, OwnedServerName)> {
|
||||
let mut make_join_response_and_server =
|
||||
Err!(BadServerResponse("No server available to assist in joining."));
|
||||
|
||||
let mut make_join_counter: usize = 0;
|
||||
let mut incompatible_room_version_count: usize = 0;
|
||||
let mut make_join_counter: usize = 1;
|
||||
|
||||
for remote_server in servers {
|
||||
if services.globals.server_is_ours(remote_server) {
|
||||
continue;
|
||||
}
|
||||
info!("Asking {remote_server} for make_join ({make_join_counter})");
|
||||
info!(
|
||||
"Asking {remote_server} for make_join (attempt {make_join_counter}/{})",
|
||||
servers.len()
|
||||
);
|
||||
let make_join_response = services
|
||||
.sending
|
||||
.send_federation_request(
|
||||
@@ -1012,44 +832,56 @@ async fn make_join_request(
|
||||
trace!("make_join response: {:?}", make_join_response);
|
||||
make_join_counter = make_join_counter.saturating_add(1);
|
||||
|
||||
if let Err(ref e) = make_join_response {
|
||||
if matches!(
|
||||
e.kind(),
|
||||
ErrorKind::IncompatibleRoomVersion { .. } | ErrorKind::UnsupportedRoomVersion
|
||||
) {
|
||||
incompatible_room_version_count =
|
||||
incompatible_room_version_count.saturating_add(1);
|
||||
}
|
||||
|
||||
if incompatible_room_version_count > 15 {
|
||||
info!(
|
||||
"15 servers have responded with M_INCOMPATIBLE_ROOM_VERSION or \
|
||||
M_UNSUPPORTED_ROOM_VERSION, assuming that conduwuit does not support the \
|
||||
room version {room_id}: {e}"
|
||||
);
|
||||
make_join_response_and_server =
|
||||
Err!(BadServerResponse("Room version is not supported by Conduwuit"));
|
||||
return make_join_response_and_server;
|
||||
}
|
||||
|
||||
if make_join_counter > 40 {
|
||||
warn!(
|
||||
"40 servers failed to provide valid make_join response, assuming no server \
|
||||
can assist in joining."
|
||||
);
|
||||
make_join_response_and_server =
|
||||
Err!(BadServerResponse("No server available to assist in joining."));
|
||||
|
||||
return make_join_response_and_server;
|
||||
}
|
||||
}
|
||||
|
||||
make_join_response_and_server = make_join_response.map(|r| (r, remote_server.clone()));
|
||||
|
||||
if make_join_response_and_server.is_ok() {
|
||||
break;
|
||||
match make_join_response {
|
||||
| Ok(response) => {
|
||||
info!("Received make_join response from {remote_server}");
|
||||
if let Err(e) = validate_remote_member_event_stub(
|
||||
&MembershipState::Join,
|
||||
sender_user,
|
||||
room_id,
|
||||
&to_canonical_object(&response.event)?,
|
||||
) {
|
||||
warn!("make_join response from {remote_server} failed validation: {e}");
|
||||
continue;
|
||||
}
|
||||
return Ok((response, remote_server.clone()));
|
||||
},
|
||||
| Err(e) => match e.kind() {
|
||||
| ErrorKind::UnableToAuthorizeJoin => {
|
||||
info!(
|
||||
"{remote_server} was unable to verify the joining user satisfied \
|
||||
restricted join requirements: {e}. Will continue trying."
|
||||
);
|
||||
},
|
||||
| ErrorKind::UnableToGrantJoin => {
|
||||
info!(
|
||||
"{remote_server} believes the joining user satisfies restricted join \
|
||||
rules, but is unable to authorise a join for us. Will continue trying."
|
||||
);
|
||||
},
|
||||
| ErrorKind::IncompatibleRoomVersion { room_version } => {
|
||||
warn!(
|
||||
"{remote_server} reports the room we are trying to join is \
|
||||
v{room_version}, which we do not support."
|
||||
);
|
||||
return Err(e);
|
||||
},
|
||||
| ErrorKind::Forbidden { .. } => {
|
||||
warn!("{remote_server} refuses to let us join: {e}.");
|
||||
return Err(e);
|
||||
},
|
||||
| ErrorKind::NotFound => {
|
||||
info!(
|
||||
"{remote_server} does not know about {room_id}: {e}. Will continue \
|
||||
trying."
|
||||
);
|
||||
},
|
||||
| _ => {
|
||||
info!("{remote_server} failed to make_join: {e}. Will continue trying.");
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
make_join_response_and_server
|
||||
info!("All {} servers were unable to assist in joining {room_id} :(", servers.len());
|
||||
Err!(BadServerResponse("No server available to assist in joining."))
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
result::FlatOk,
|
||||
trace,
|
||||
utils::{self, shuffle, stream::IterStream},
|
||||
utils::{self, shuffle, stream::IterStream, to_canonical_object},
|
||||
warn,
|
||||
};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
@@ -253,7 +253,6 @@ async fn knock_room_by_id_helper(
|
||||
room_id,
|
||||
reason.clone(),
|
||||
servers,
|
||||
None,
|
||||
&None,
|
||||
)
|
||||
.await
|
||||
@@ -741,6 +740,17 @@ async fn make_knock_request(
|
||||
|
||||
trace!("make_knock response: {make_knock_response:?}");
|
||||
make_knock_counter = make_knock_counter.saturating_add(1);
|
||||
if let Ok(r) = &make_knock_response {
|
||||
if let Err(e) = validate_remote_member_event_stub(
|
||||
&MembershipState::Knock,
|
||||
sender_user,
|
||||
room_id,
|
||||
&to_canonical_object(&r.event)?,
|
||||
) {
|
||||
warn!("make_knock response from {remote_server} failed validation: {e}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
make_knock_response_and_server = make_knock_response.map(|r| (r, remote_server.clone()));
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ pub(crate) fn validate_remote_member_event_stub(
|
||||
};
|
||||
if event_membership != &membership.as_str() {
|
||||
return Err!(BadServerResponse(
|
||||
"Remote server returned member event with incorrect room_id"
|
||||
"Remote server returned member event with incorrect membership type"
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, at, debug_warn,
|
||||
Err, Error, Result, at, debug_warn,
|
||||
matrix::{
|
||||
event::{Event, Matches},
|
||||
pdu::PduCount,
|
||||
@@ -26,7 +26,7 @@
|
||||
DeviceId, RoomId, UserId,
|
||||
api::{
|
||||
Direction,
|
||||
client::{filter::RoomEventFilter, message::get_message_events},
|
||||
client::{error::ErrorKind, filter::RoomEventFilter, message::get_message_events},
|
||||
},
|
||||
events::{
|
||||
AnyStateEvent, StateEventType,
|
||||
@@ -279,23 +279,30 @@ pub(crate) async fn ignored_filter(
|
||||
|
||||
is_ignored_pdu(services, pdu, user_id)
|
||||
.await
|
||||
.unwrap_or(true)
|
||||
.eq(&false)
|
||||
.then_some(item)
|
||||
}
|
||||
|
||||
/// Determine whether a PDU should be ignored for a given recipient user.
|
||||
/// Returns True if this PDU should be ignored, returns False otherwise.
|
||||
///
|
||||
/// The error SenderIgnored is returned if the sender or the sender's server is
|
||||
/// ignored by the relevant user. If the error cannot be returned to the user,
|
||||
/// it should equate to a true value (i.e. ignored).
|
||||
#[inline]
|
||||
pub(crate) async fn is_ignored_pdu<Pdu>(
|
||||
services: &Services,
|
||||
event: &Pdu,
|
||||
recipient_user: &UserId,
|
||||
) -> bool
|
||||
) -> Result<bool>
|
||||
where
|
||||
Pdu: Event + Send + Sync,
|
||||
{
|
||||
// exclude Synapse's dummy events from bloating up response bodies. clients
|
||||
// don't need to see this.
|
||||
if event.kind().to_cow_str() == "org.matrix.dummy_event" {
|
||||
return true;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let sender_user = event.sender();
|
||||
@@ -310,21 +317,27 @@ pub(crate) async fn is_ignored_pdu<Pdu>(
|
||||
|
||||
if !type_ignored {
|
||||
// We cannot safely ignore this type
|
||||
return false;
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if server_ignored {
|
||||
// the sender's server is ignored, so ignore this event
|
||||
return true;
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::SenderIgnored { sender: None },
|
||||
"The sender's server is ignored by this server.",
|
||||
));
|
||||
}
|
||||
|
||||
if user_ignored && !services.config.send_messages_from_ignored_users_to_client {
|
||||
// the recipient of this PDU has the sender ignored, and we're not
|
||||
// configured to send ignored messages to clients
|
||||
return true;
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::SenderIgnored { sender: Some(event.sender().to_owned()) },
|
||||
"You have ignored this sender.",
|
||||
));
|
||||
}
|
||||
|
||||
false
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -371,11 +384,3 @@ pub(crate) async fn is_ignored_invite(
|
||||
.invite_filter_level(&sender_user, recipient_user)
|
||||
.await == FilterLevel::Ignore
|
||||
}
|
||||
|
||||
#[cfg_attr(debug_assertions, ctor::ctor)]
|
||||
fn _is_sorted() {
|
||||
debug_assert!(
|
||||
IGNORED_MESSAGE_TYPES.is_sorted(),
|
||||
"IGNORED_MESSAGE_TYPES must be sorted by the developer"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Result, at, debug_warn,
|
||||
Err, Result, at, debug_warn, err,
|
||||
matrix::{Event, event::RelationTypeEqual, pdu::PduCount},
|
||||
utils::{IterStream, ReadyExt, result::FlatOk, stream::WidebandExt},
|
||||
};
|
||||
@@ -18,7 +18,7 @@
|
||||
events::{TimelineEventType, relation::RelationType},
|
||||
};
|
||||
|
||||
use crate::Ruma;
|
||||
use crate::{Ruma, client::is_ignored_pdu};
|
||||
|
||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}`
|
||||
pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
|
||||
@@ -118,6 +118,14 @@ async fn paginate_relations_with_filter(
|
||||
debug_warn!(req_evt = %target, %room_id, "Event relations requested by {sender_user} but is not allowed to see it, returning 404");
|
||||
return Err!(Request(NotFound("Event not found.")));
|
||||
}
|
||||
let target_pdu = services
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu(target)
|
||||
.await
|
||||
.map_err(|_| err!(Request(NotFound("Event not found."))))?;
|
||||
// Return M_SENDER_IGNORED if the sender of base_event is ignored (MSC4406)
|
||||
is_ignored_pdu(services, &target_pdu, sender_user).await?;
|
||||
|
||||
let start: PduCount = from
|
||||
.map(str::parse)
|
||||
@@ -159,6 +167,7 @@ async fn paginate_relations_with_filter(
|
||||
.ready_take_while(|(count, _)| Some(*count) != to)
|
||||
.take(limit)
|
||||
.wide_filter_map(|item| visibility_filter(services, sender_user, item))
|
||||
.wide_filter_map(|item| ignored_filter(services, item, sender_user))
|
||||
.then(async |mut pdu| {
|
||||
if let Err(e) = services
|
||||
.rooms
|
||||
@@ -214,3 +223,17 @@ async fn visibility_filter<Pdu: Event + Send + Sync>(
|
||||
.await
|
||||
.then_some(item)
|
||||
}
|
||||
|
||||
async fn ignored_filter<Pdu: Event + Send + Sync>(
|
||||
services: &Services,
|
||||
item: (PduCount, Pdu),
|
||||
sender_user: &UserId,
|
||||
) -> Option<(PduCount, Pdu)> {
|
||||
let (_, pdu) = &item;
|
||||
|
||||
if is_ignored_pdu(services, pdu, sender_user).await.ok()? {
|
||||
None
|
||||
} else {
|
||||
Some(item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ pub(crate) async fn get_room_event_route(
|
||||
|
||||
let (mut event, visible) = try_join(event, visible).await?;
|
||||
|
||||
if !visible || is_ignored_pdu(services, &event, body.sender_user()).await {
|
||||
if !visible || is_ignored_pdu(services, &event, body.sender_user()).await? {
|
||||
return Err!(Request(Forbidden("You don't have permission to view this event.")));
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ pub(super) async fn ldap_login(
|
||||
) -> Result<OwnedUserId> {
|
||||
let (user_dn, is_ldap_admin) = match services.config.ldap.bind_dn.as_ref() {
|
||||
| Some(bind_dn) if bind_dn.contains("{username}") =>
|
||||
(bind_dn.replace("{username}", lowercased_user_id.localpart()), false),
|
||||
(bind_dn.replace("{username}", lowercased_user_id.localpart()), None),
|
||||
| _ => {
|
||||
debug!("Searching user in LDAP");
|
||||
|
||||
@@ -144,12 +144,16 @@ pub(super) async fn ldap_login(
|
||||
.await?;
|
||||
}
|
||||
|
||||
let is_conduwuit_admin = services.admin.user_is_admin(lowercased_user_id).await;
|
||||
// Only sync admin status if LDAP can actually determine it.
|
||||
// None means LDAP cannot determine admin status (manual config required).
|
||||
if let Some(is_ldap_admin) = is_ldap_admin {
|
||||
let is_conduwuit_admin = services.admin.user_is_admin(lowercased_user_id).await;
|
||||
|
||||
if is_ldap_admin && !is_conduwuit_admin {
|
||||
Box::pin(services.admin.make_user_admin(lowercased_user_id)).await?;
|
||||
} else if !is_ldap_admin && is_conduwuit_admin {
|
||||
Box::pin(services.admin.revoke_admin(lowercased_user_id)).await?;
|
||||
if is_ldap_admin && !is_conduwuit_admin {
|
||||
Box::pin(services.admin.make_user_admin(lowercased_user_id)).await?;
|
||||
} else if !is_ldap_admin && is_conduwuit_admin {
|
||||
Box::pin(services.admin.revoke_admin(lowercased_user_id)).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(user_id)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user