Compare commits

...

304 Commits

Author SHA1 Message Date
Jason Volk b8b93a2e86 Bump 0.4.1
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk 29d69b7688 update complement test results
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
strawberry bd07fb61e0 add hot_reload.md to SUMMARY.md
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry a41a60ef07 media: dont ignore requested filename on /download for Content-Disposition
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
Jason Volk ec7a9ab726 add toolchain and build/check shortcut to smoketest
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk 25f598ce6c enable http2 feature for reqwest.
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk dbcb3be0ab fix duplicate output; increase wait in smoketest.
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk a537462d51 replace num_cpus dependency with available_parallelism()
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk d2aef071bc add possibly referenced rocksdb symbol to export list.
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk d68b11e8ff fix rustflags for release-max-perf
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk 9cf5b0926e fix regressed jemalloc stats feature
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk ff0b57c89c remove unused jemalloc dep in main module.
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk b94045a468 dissolve key_value/*
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk 3122648767 split ruma_wrapper from_request() related.
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk 3f5349ad76 simplify RumaHandler for Router building.
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk 27dcf213f1 tweak error strings.
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk a1b526b3b7 tweak log levels
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk dc614e11d6 check invite target is our server.
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk c5569b4c6e dedup acl checks
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
Jason Volk 71a1285c7b hoist receipt ACL check
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-27 18:16:23 -04:00
strawberry abdda6cf32 check invited user's server against ACLs on /invite
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry 4d21f9d962 use ok_or_else instead of ok_or for function calls in server_server.rs
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry 1013fe5a42 check for membership join state at /send_join
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry f31b7b9420 ignore inbound EDUs for users that dont belong to origin server
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry e5e358cc68 compare X-Matrix origin + body origin and check PDU/EDU length at /send txn
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry 50bc7cc005 check state_key matches sender user at /send_leave
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry 445015e9ea check user ID server against ACLs at /send_leave
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry 7a38c12e5d check for member event type at /send_leave
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry 2a77951152 check for membership leave state at /send_leave
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry 0256c27363 check if we know about room at /make_leave
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry 826edc0a3a check state_key matches sender user at /send_join
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry a5043a38e1 only allow membership event types at /send_join
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry bfd471a863 check user ID server against ACLs for /send_join
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry 3981e77ec6 check user ID server against ACLs for /make_join
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry 81bf4b7150 check user ID server against ACLs for /make_leave
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry b8ec763a7c ignore read receipts from ACL'd servers and users not joined
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry 003d4edbfa debug log receiving typing EDUs for users not in room
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry 4f0006d18a ignore typing EDUs from ACL'd user's servers
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry b822e3a94c listen on IPv6 localhost by default
this is dual-stack by default on linux, resolves
issues with nginx using `localhost` and randomly
choosing between 127.0.0.1 and [::1], causing
intermittent upstream issues

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry 68fffe8e96 check room ACLs on sender user's server for incoming PDUs
`handle_incoming_pdu`

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
strawberry 7328ed7509 rename misleading sender_servername to origin
this is the X-Matrix origin/server, NOT the `"sender"``
user's server name.

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-27 18:16:23 -04:00
Benjamin Lee 6ccf578437 bump rocksdb input
Nedded to pull in [1], which is rared for dynamic rocksdb builds with
liburing.

[1]: https://github.com/girlbossceo/rocksdb/commit/c8a1450231e9c608edf535538dbe8ca1a8d2f3bc
2024-05-27 04:54:57 -04:00
Benjamin Lee 8a1848a814 Revert "nix: default output to scopeHostStatic instead of scopeHost"
This reverts commit a37b2b9e64.

Dynamic builds are working again, so we'd prefer having that be the
default output for consistency with nixpkgs.
2024-05-27 04:54:57 -04:00
Benjamin Lee b4cd8e9140 fix dynamic builds with liburing
The original implementation of this was really weird, so I restructed it
a lot while debugging, and am just gonna leave the restructured version.

Root cause of the segfault seems to be that upstream nixpkgs liburing
derivation is generating both static and dynamic libraries, causing
rocksdb to statically link liburing in a dynamic build, pulling in some
allocator stuff at the same time. I created a PR[1] to fix this upstream,
but it probably won't be available on nixos-unstable for quite a while,
so we can also patch it locally.

[1]: https://github.com/NixOS/nixpkgs/pull/314945
2024-05-27 04:54:57 -04:00
Benjamin Lee a08f90b161 add a smoke-test to CI for the nix 'default' output
I talked to somebody yesterday in #conduwuit:puppygock.gay that was using
this output in their system config. The dynamically-linked jemalloc build
is quite fragile, and is not tested by anything else in CI. We want to
make sure we don't break it again in the future.
2024-05-27 04:54:57 -04:00
Benjamin Lee 207979579c fix dynamically linked devshell
This failed to inherit the fix from bec507d739
because the crane package's buildInputs become propagatedBuildInputs in
a static stdenv, but become normal buildInputs in a dynamic stdenv. Since
we were only pulling propagatedBuildInputs into the devshell, dynamically
linked devshells did not include the rust-jemalloc-sys package. This
causes tikv-jemalloc-sys to build it's own static jemalloc package, and
we end up loading libc before jemalloc at runtime.
2024-05-27 04:54:57 -04:00
Benjamin Lee 68b96026ec unmark dynamically-linked jemalloc builds as broken
It turns out that this was actually fixed by
bec507d739 and
857ac42aac, but we didn't identify it at the
time. Notably, the `dynamic` devshell is still broken.
2024-05-27 04:54:57 -04:00
strawberry 30beb20230 conditionally static link rust-rocksdb-uwu by hot reload cfg
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 19e7779693 update complement test results
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 6269822613 actually fix all let_underscore_must_use lints
CI caught some more

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 0877ee6191 allow let underscore use lint for rocksdb create cf for now
the workaround needs to be extended to rocksdb caches, but
i dont know that part of code

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry a37b2b9e64 nix: default output to scopeHostStatic instead of scopeHost
defaults to static builds instead of dynamically linked builds

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 29fe960efa bump hyper-util and libz-sys
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 6bf2e73830 ci: run cache dependencies in ci.yml as well
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 630760b5da bump rocksdb to v9.2.1
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 61e7f1e614 remove rpath = true from dev profile as the rustflags have it
needed for hot reloading but rpath being true by default
causes linker errors on lld because of the sad rpath bug

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 7ebed7aa3e clarify disable-room message after banning room
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry ad3eeaf4c1 delete audit.toml
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 5215fbe695 drop redaction calculated hash log to debug_info
this is normal redactions. no valid reason this needs to be
warn as it just causes confusion.

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry dc9fe657d5 fix guest accounts being logged still
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 1c7c5bc09c feat: add /_conduwuit/local_user_count endpoint
only enabled if federation is enabled

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 32161801ed use/enable let_underscore_must_use lint
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 71bdcb958a fix: dont drop remote federation error on 4xx responses
for a very long time, if a remote server responded to us with
a valid but unsuccessful (HTTP 4xx) response and the caller was the
`send_federation_request` function, we may find ourselves
with a warning message only containing the destination's
server name which was very unhelpful. the true error was
buried away in trace logs. this would primarily be noticed
with server key fetch requests from us.

conduit has been throwing away the ruma request error: https://gitlab.com/famedly/conduit/-/blame/next/src/utils/error.rs#L62

before: 2024-05-23T04:45:02.930224Z  WARN router:{path=/_matrix/client/v3/publicRooms}:handle: conduit_api::client_server::directory: Failed to return our /publicRooms: matrix.org
after: 2024-05-23T05:05:02.435272Z  WARN router:{path=/_matrix/client/v3/publicRooms}:handle: conduit_api::client_server::directory: Failed to return our /publicRooms: matrix.org: [401 / M_UNAUTHORIZED] Failed to find any key to satisfy: _FetchKeyRequest(server_name='your.server.name', minimum_valid_until_ts=1716440702337, key_ids=['ed25519:RQB3XPQX'])

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry d3db0ad4e2 renovate: label PRs as dependencies and github_actions
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry e098448b9d init a few vecs in event_handler using with_capacity
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry d49507bc21 media: decomplexify get_all_media_keys for deleting all MXC URIs
wow this was terrible, early strawberry code

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry cb73ae3732 add registration token validity endpoint as per matrix 1.2
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 06bec40591 fix: add missing fetch_required_signing_keys for remote send_leave
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 9a7ba94ccf explicity define unstable support for sliding sync
this matrix-react-sdk PR (and the cited sliding sync MSC)
says that they will intend on checking sliding sync support
from this unstable feature flag at /versions until the CORS
header stuff is specced

https://github.com/matrix-org/matrix-react-sdk/pull/12498

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 2990c30ac9 nix: bump rocksdb input
• Updated input 'rocksdb':
    'github:girlbossceo/rocksdb/db6df0b185774778457dabfcbd822cb81760cade' (2024-05-03)
  → 'github:girlbossceo/rocksdb/be68b3c95ccd225f3121ba33a67cfaf3c3596afc' (2024-05-23)

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry d9c575d96f bump deps
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry c32406aa0e replace deprecated config option for complement
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 03d12cb44e update docs a tad
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry bef7dbd1cb finally error on complement diff mismatch, remove jemalloc builds from
CI

jemalloc is now a default feature

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 08577873b4 update complement test results
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry a3931b0f1f nix: bump flake.lock
• Updated input 'crane':
    'github:ipetkov/crane/27025ab71bdca30e7ed0a16c88fd74c5970fc7f5' (2024-05-09)
  → 'github:ipetkov/crane/7443df1c478947bf96a2e699209f53b2db26209d' (2024-05-19)
• Updated input 'fenix':
    'github:nix-community/fenix/297c756ba6249d483c1dafe42378560458842173' (2024-05-10)
  → 'github:nix-community/fenix/063d7e5fac454edd35b7e2cedb6ca9fb1410c79b' (2024-05-21)
• Updated input 'fenix/rust-analyzer-src':
    'github:rust-lang/rust-analyzer/5bf2f85c8054d80424899fa581db1b192230efb5' (2024-05-09)
  → 'github:rust-lang/rust-analyzer/21ec8f523812b88418b2bfc64240c62b3dd967bd' (2024-05-19)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/f1010e0469db743d14519a1efd37e23f8513d714' (2024-05-09)
  → 'github:NixOS/nixpkgs/3eaeaeb6b1e08a016380c279f8846e0bd8808916' (2024-05-21)

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry ba2f22b5d3 nix: remove jemalloc (now default) targets, add jq input for default
jq input change was from https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/17eb3545906d21f2ed18f0f0f917a4638f12ef6c
to prevent unnecessary bindgen rebuilds

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry 0914aaa1b6 skip a few known flaky/unreliable complement tests
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-25 22:28:43 -04:00
strawberry f3427afc7f nix: use new public keys for binary caches due to attic issues
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-24 18:13:02 -04:00
morguldir 9aa372d83b nix: Allow excluding features, allow disabling release_max_log_level 2024-05-24 15:12:23 -04:00
morguldir 5893901a75 Explicitly include snappy as well
Not sure what changed that we need this

Signed-off-by: morguldir <morguldir@protonmail.com>
2024-05-24 12:46:15 -04:00
morguldir 8ba9b33a95 Make sure we use the liburing of the platform we're building for
Signed-off-by: morguldir <morguldir@protonmail.com>
2024-05-24 12:46:15 -04:00
morguldir 70047ff26d Make rocksdb include liburing, and tell gcc the path during the build
With: strings /nix/store/9skicdac6xs4yww1nd3h7m6xydv4hxlj-rocksdb-9.1.1/lib/librocksdb.so.9|rg io_uring|wc -l
112
With: strings result/bin/conduit |rg io_uring|wc -l
5

Without: strings static-x86_64-unknown-linux-musl-jemalloc |rg io_uring | wc -l
0

Signed-off-by: morguldir <morguldir@protonmail.com>
2024-05-24 12:46:15 -04:00
Benjamin Lee 1d57e14dc0 set C/LDFLAGS for complement dependencies directly
Previously we were relying on NIX_CFLAGS_COMPILE, but this is not being
set in static devshells. A cleaner solution for complement would likely
be to build the tests in their own nix derivation instead of building
them in the devshell, but this change unblocks CI for now.
2024-05-24 10:53:47 -04:00
Benjamin Lee 5d81203277 use a statically-linked binary for complement
Dynamically-linked jemalloc is broken.
2024-05-24 10:53:47 -04:00
Benjamin Lee ad39a34c16 add a dynamically-linked devshell
This is broken on linux, but can be used by darwin users for development,
since static/jemalloc/darwin is broken.
2024-05-24 10:53:47 -04:00
Benjamin Lee a007338b34 mark dynamic jemalloc builds as broken on linux 2024-05-24 10:53:47 -04:00
Benjamin Lee 3d1507e6dd mark static rocksdb broken on darwin 2024-05-24 10:53:47 -04:00
Benjamin Lee 4cb7c0b982 don't use prefixed jemalloc with rocksdb
This is causing build failures on Mac:

> In file included from /tmp/nix-build-rocksdb-static-aarch64-apple-darwin-9.1.1.drv-0/source/memory/memory_allocator.cc:8:
> In file included from /tmp/nix-build-rocksdb-static-aarch64-apple-darwin-9.1.1.drv-0/source/memory/jemalloc_nodump_allocator.h:11:
> /tmp/nix-build-rocksdb-static-aarch64-apple-darwin-9.1.1.drv-0/source/port/jemalloc_helper.h:63:36: warning: unknown attribute '_rjem_malloc' ignored [-Wunknown-attributes]
> mallocx(size_t, int) JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1)
>                                    ^~~~~~
> /nix/store/3bix0kzy670dyhhizri3dwb1qfj3sdpa-jemalloc-static-aarch64-apple-darwin-5.3.0/include/jemalloc/jemalloc.h:412:18: note: expanded from macro 'malloc'
> #  define malloc je_malloc
>                  ^~~~~~~~~
> /nix/store/3bix0kzy670dyhhizri3dwb1qfj3sdpa-jemalloc-static-aarch64-apple-darwin-5.3.0/include/jemalloc/jemalloc.h:75:21: note: expanded from macro 'je_malloc'
> #  define je_malloc _rjem_malloc
>                     ^~~~~~~~~~~~
> /nix/store/3bix0kzy670dyhhizri3dwb1qfj3sdpa-jemalloc-static-aarch64-apple-darwin-5.3.0/include/jemalloc/jemalloc.h:183:43: note: expanded from macro 'JEMALLOC_ATTR'
> #  define JEMALLOC_ATTR(s) __attribute__((s))

Full build log at <https://girlboss.ceo/~strawberry/pb/ygJ3>. This is
likely fixable with patches to rocksdb, but not worth it since darwin is
only a dev platform.
2024-05-24 10:53:47 -04:00
Benjamin Lee 0c34cf95ce set show-trace for nix in CI 2024-05-24 10:53:47 -04:00
Benjamin Lee 17cc02ff99 add a 'no-features' devshell for local testing 2024-05-24 10:53:47 -04:00
Benjamin Lee c0f8253fc5 enable all-features in nix for CI builds
CI is running `cargo build --all-features`, so we should be passing all
the features to nix as well.

The only thing this currently affects is the jemalloc_prof feature, but if
we add any non-default features that affect nix in the future they should
also be handled correctly now.
2024-05-24 10:53:47 -04:00
Benjamin Lee 0fd0a5d73c switch default devshell to static linking
Dynamically-linked jemalloc doesn't work due to link-order issues, and we
want CI to be testing a static binary anyway since that's what we're
publishing in releases.
2024-05-24 10:53:47 -04:00
Benjamin Lee 4e6fc2f2df factor devshell out into a helper function
We're planning to add a second devshell with `all-features` for CI.
2024-05-24 10:53:47 -04:00
Benjamin Lee a6742ce8a7 remove liburing from devshell
This doesn't seem to be necessary to build, and the derivation is broken
in pkgsStatic.
2024-05-24 10:53:47 -04:00
Benjamin Lee 188dea13e0 do default-feature unification in nix
Some of the features affect nix dependencies, so we need to have a
full feature list available when constructing the nix derivation. This
incidentally fixes the bug where we weren't enabling jemalloc on rocksdb
in CI/devshells, because jemalloc is now a default feature. It does not
fix the more general class of that issue, where CI is performing an
`--all-features` build in a nix devshell built for default-features.

I am now passing `--no-default-features` to cargo, and having it use our
unified feature list rather than duplicating the unification inside cargo.
2024-05-24 10:53:47 -04:00
Benjamin Lee a7fe434086 only link to one jemalloc build
Without setting JEMALLOC_OVERRIDE, we end up linking to two different
jemalloc builds. Once dynamically, as a transitive dependency through
rocksdb, and a second time to the static jemalloc that tikv-jemalloc-sys
builds.
2024-05-24 10:53:47 -04:00
renovate[bot] eb8dd9cb44 chore(deps): update aquasecurity/trivy-action action to v0.21.0 2024-05-23 01:30:36 -04:00
strawberry 474d50d10c bump conduwuit version to 0.4.0
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-21 20:22:17 -04:00
Jason Volk 2e732c711c docs: Update docs for hot-reloading.
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-21 20:22:17 -04:00
strawberry 981ec51ec0 docs: add initial docs for hot reload
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-21 20:22:17 -04:00
Jason Volk 2dd5cf8c68 move clap; fix version
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-21 20:22:17 -04:00
Jason Volk 74832bdc47 fix smoke from builds produced by --all-features
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-21 20:22:17 -04:00
Jason Volk fdc9a9a1b8 add cargo smoketest
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-21 20:22:17 -04:00
Jason Volk 1f3a9a40e5 lint clippy::collapsible_match (nightly)
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-21 20:22:17 -04:00
Jason Volk 362649ff87 rename src/bin to src/main
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-21 20:22:17 -04:00
strawberry 4aeec78ab4 debian: remove old symlink on postrm
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-21 20:22:17 -04:00
strawberry 9bfa89a555 adjust debian metadata, set crane workspace name
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-21 20:22:17 -04:00
Jason Volk 6c1434c165 Hot-Reloading Refactor
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-21 20:22:17 -04:00
slonkazoid ae1a4fd283 add modification time fallback if birth time is not supported on this platform 2024-05-21 16:58:30 -04:00
Benjamin Lee 9eb0784f6f don't return extra member count or e2ee device updates from sync
Previously, we were returning redundant member count updates or encrypted
device updates from the /sync endpoint in some cases. The extra member
count updates are spec-compliant, but unnecessary, while the extra
encrypted device updates violate the spec.

The refactor necessary to fix this bug is also necessary to support
filtering on state events in sync.

Details:

Joined room incremental sync needs to examine state events for four
purposes:

 1. determining whether we need to return an update to room member counts
 2. determining the set of left/joined devices for encrypted rooms
    (returned in `device_lists`)
 3. returning state events to the client (in `rooms.joined.*.state`)
 4. tracking which member events we have sent to the client, so they can
    be omitted on future requests when lazy-loading is enabled.

The state events that we need to examine for the first two cases is member
events in the delta between `since` and the end of `timeline`. For the
second two cases, we need the delta between `since` and the start of
`timeline`, plus contextual member events for any senders that occur in
`timeline`. The second list is subject to filtering, while the first is
not.

Before this change, we were using the same set of state events that we are
returning to the client (cases 3/4) to do the analysis for cases 1/2.
In a compliant implementation, this would result in us missing some
relevant member events in 1/2 in addition to seeing redundant member
events. In current conduwuit this is not the case because the set of
events that we return to the client is always a superset of the set that
is needed for cases 1/2. This is because we don't support filtering, and
we have an existing bug[1] where we are returning the delta between
`since` and the end of `timeline` rather than the start.

[1]: https://github.com/girlbossceo/conduwuit/issues/361

Fixing this is necessary to implement filtering because otherwise
we would start missing some member events for member count or encrypted
device updates if the relevant member events are rejected by the filter.
This would be much worse than our current behavior.
2024-05-20 20:55:56 -04:00
Benjamin Lee 8bffcfe82b remove sync response cache
This cache can serve invalid responses, and has an extremely low hit
rate.

It serves invalid responses because because it's only keyed off
the `since` parameter, but many of the other request parameters also
affect the response or it's side effects. This will become worse once we
implement filtering, because there will be a wider space of parameters
with different responses. This problem is fixable, but not worth it
because of the low hit rate.

The low hit rate is because normal clients will always issue the next
sync request with `since` set to the `prev_batch` value of the previous
response. The only time we expect to see multiple requests with the same
`since` is when the response is empty, but we don't cache empty
responses.

This was confirmed experimentally by logging cache hits and misses over
15 minutes with a wide variety of clients. This test was run on
matrix.computer.surgery, which has only a few active users, but a
large volume of sync traffic from many rooms. Over the test period, we
had 3 hits and 5309 misses. All hits occurred in the first minute, so I
suspect that they had something to do with client recovery from an
offline state. The clients that were connected during the test are:

 - element web
 - schildichat web
 - iamb
 - gomuks
 - nheko
 - fractal
 - fluffychat web
 - fluffychat android
 - cinny web
 - element android
 - element X android

Fixes: #336
2024-05-17 18:13:11 -04:00
strawberry 6ef4781050 downgrade zlib/libz-sys to 1.1.16 as it breaks nix
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-17 03:42:25 -04:00
strawberry 302592f219 bump conduwuit version to 0.3.4
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-17 03:17:27 -04:00
Benjamin Lee 7cd72d8447 bump lockfile 2024-05-17 03:08:56 -04:00
renovate[bot] 4389e08686 chore(deps): update cachix/install-nix-action action to v27 2024-05-15 14:39:21 -04:00
strawberry 91064fe873 fix up systemd unit file, remove chown on config file for debian
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry 004354353a docker-compose: slight cleanups, correct database paths, fix branding
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry c64a507691 correct default database path to /var/lib/conduwuit
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry 81d2078cdb debian: dont start service immediately, add postinst instructions
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry f5864afb52 remove namespace check on username login, code simplification on login route
the namespace check on username login is unnecessary, hashes aren't ever
going to match, and axum auth handles this kind of stuff already

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry 9a63e7cc9b flip order of complement diff checking, update test results
we now pass all Content-Disposition checks/tests

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry 296d7c58ee nix: bump complement input for conduwuit support
https://github.com/matrix-org/complement/pull/723

• Updated input 'complement':
    'github:matrix-org/complement/370a014dca0f720614e0c8f68b9a3e66ecf7f516' (2024-05-02)
  → 'github:matrix-org/complement/8587fb3cbe746754b2c883ff6c818ca4d987d0a5' (2024-05-14)

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry a8446f910a debian: fix config permissions, delete debconf support
debconf support needs to be done in a way that does not duplicate
the config file like upstream does.

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry a063a6d088 debian: make the docs actually coherent and understandable, and update it
the language here is very poor and i'm not sure why it was written like this.

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry 5069c88f77 ci: correct paths for debian package creation, use conduwuit
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry 53974320e5 debian: create system account verbosely
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry 1c6ef66e3e fix gitlab ci
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry ffb63c9c8d ci: regex out the cargo/rustc target for cargo-deb
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry de6b296eb5 ci: use verbose for mv operations
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry 4c11c9f048 ci: use target-specific dirs for cargo-deb, fix cargo-deb paths
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry 6074298426 ci: allow build job to be ran for all events except for draft PRs
this allows build to be ran for workflow_dispatch

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry 6e9f68bf81 chore: update complement test results
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry edd67a102a ci(debian): add missing --target= for arm64 debs, add --verbose
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-15 14:31:35 -04:00
strawberry 434b5118cc media: return our detected MIME type for Content-Type
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 15:54:22 -04:00
strawberry 4185a33747 fix: we should be checking for xml MIME type instead
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 15:54:22 -04:00
strawberry 829307c83b disallow svg MIME types to be inline Content-Disposition
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 11:37:50 -04:00
strawberry 2bd7a92256 complement: add -tags="conduwuit_blacklist"
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry bfa33f8713 unpin rust-rocksdb version
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry 040cf29051 ci: add lix binary cache, update .gitlab-ci file
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry 80bc1cd78a ci: output 100 failure summary lines instead of 50
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry 78994deb1e nix: simplify isDarwin lib check
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry 714b3e7144 s/nix/lix in a couple places
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry 1cd57f40f6 upload complement OCI image from CI, document where it can be found, use main instead of dev for tag
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry da9a0eb77b docs: fix broken systemd unit link
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry 37b2c90e62 chore(nix): bump flake
• Updated input 'complement':
    'github:matrix-org/complement/891d18872c153d39a9ce63b545045efddb845738' (2024-04-30)
  → 'github:matrix-org/complement/370a014dca0f720614e0c8f68b9a3e66ecf7f516' (2024-05-02)
• Updated input 'crane':
    'github:ipetkov/crane/f6c6a2fb1b8bd9b65d65ca9342dd0eb180a63f11' (2024-04-21)
  → 'github:ipetkov/crane/27025ab71bdca30e7ed0a16c88fd74c5970fc7f5' (2024-05-09)
• Updated input 'fenix':
    'github:nix-community/fenix/73124e1356bde9411b163d636b39fe4804b7ca45' (2024-05-01)
  → 'github:nix-community/fenix/297c756ba6249d483c1dafe42378560458842173' (2024-05-10)
• Updated input 'fenix/rust-analyzer-src':
    'github:rust-lang/rust-analyzer/55d9a533b309119c8acd13061581b43ae8840823' (2024-04-20)
  → 'github:rust-lang/rust-analyzer/5bf2f85c8054d80424899fa581db1b192230efb5' (2024-05-09)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/5c24cf2f0a12ad855f444c30b2421d044120c66f' (2024-04-19)
  → 'github:NixOS/nixpkgs/f1010e0469db743d14519a1efd37e23f8513d714' (2024-05-09)

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry ba150a1185 nix: stop running unnecessary cargo check on builds
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry ddce9496f2 nix: fix building rust on macOS (Security apple_sdk framework)
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry fe637f481d ci: fix incorrect startsWith syntax
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-11 14:29:00 -04:00
strawberry 18e43e1d35 Reapply "bump various deps"
This reverts commit 6b918966d4.
2024-05-10 22:56:44 -04:00
strawberry 09fca89ac5 Revert "rocksdb: enable async_io if using io_uring feature"
This reverts commit 6266e0ab5e.
2024-05-10 22:56:44 -04:00
morguldir 9f19a2025d Revert "feat(membership): check if user already has the membership that is requested to be set"
This reverts commit 321a6ca0fe.

These checks were not working as intended, resulting in the unban button not working

The join check gets kept since it slightly reduces the amount of sent joins in some cases
This check will probably be replaced soon for a more universal solution to the "made no change" issue

Signed-off-by: morguldir <morguldir@protonmail.com>
2024-05-10 22:52:44 -04:00
strawberry 6b918966d4 Revert "bump various deps"
This reverts commit 653ec3799e.
2024-05-09 22:38:05 -04:00
strawberry 328502c1cd dont send avatar url or display name for ban membership events
the display name or avatar may be offensive

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 17:44:15 -04:00
strawberry d15e461303 config option to auto-remediate bad users joining bad rooms or servers
also forgets all rooms upon leave_all_rooms

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 17:44:15 -04:00
strawberry 6946eead28 pin rust-rocksdb to before snappy update
it seems to break nix builds

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 17:42:05 -04:00
strawberry 09d3240365 bump conduwuit version to 0.3.3
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 17:42:05 -04:00
strawberry 653ec3799e bump various deps
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 17:42:05 -04:00
strawberry 6de9f52d5a docs: update differences.md
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 12:34:50 -04:00
strawberry 484e7d1d2a docs: add my selfhosted forgejo mirror
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 12:34:50 -04:00
strawberry dfa01541b3 docs: transfem.dev has rules
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 12:34:50 -04:00
strawberry adbe9268ce docs: add troubleshooting, maintenance, various improvements and fixes
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 12:34:50 -04:00
strawberry 3504e6e724 fix broken reports
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 12:33:46 -04:00
strawberry 154b2ab490 media: additional sanitisation on the Content-Disposition filename
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 09:53:04 -04:00
strawberry 2231ccf118 return inline Content-Disposition based on the detected file type (e.g. image/video)
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 09:53:04 -04:00
strawberry d4d9f92ade add security response HTTP headers if not present
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 09:53:04 -04:00
renovate[bot] e4e1636da8 chore(deps): update aquasecurity/trivy-action action to v0.20.0 2024-05-08 15:06:45 -04:00
strawberry e99aac9550 ci: fix gitlab container registry destination
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-08 15:04:59 -04:00
strawberry ddb87168ed update gitlab repo link
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-08 15:04:59 -04:00
strawberry 245c34e659 ci: dont run docker publishing if none of the usernames are set
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-07 23:59:05 -04:00
strawberry 43b07be3fc ci: use PR author instead of branch name for docker image publishing
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-07 02:44:55 -04:00
strawberry 99d98efeb1 ci: fix docker publishing typo
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 13:05:00 -04:00
strawberry 7b25ef2e6c make next_batch token a variable in search, revert threads_until change
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 1f8a7a707c nix: cache complement outputs using nix-build-and-cache
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 86ec20e787 docs: remove last dev branch mention
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
Charles Hall 8c21388f01 fix nix-build-and-cache
Now it actually caches everything.

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry d657fa32e9 ci: format string
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 321e197d8c correct arithmetic adjustments
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 16a98b0683 ci: push docker images for PRs in the merge-PR_NUMBER-HEAD_REF format, fix main pushes
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 9e1bbc1650 ci: run on new tag pushes
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 91ff6a36a4 ci: abort workflow if latest repo tag does not match with running tag ref
protects against a maintainer creating a downgrading version tag, and
uploading artifacts with that version

this check is only ran via workflow dispatch on the tag

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 56f1d8be1f ci(docker): publish latest only if ref starts with our tag format
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry ed60f189cc docs: remove dev docker images
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry cabf4362be docs: direct all PRs to main
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 2472c7c47a ci: don't run on dev anymore, run on main and non-draft PRs
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
Xiretza 136cb038cf auth_chain: add useful debug logging 2024-05-06 03:45:10 -04:00
Xiretza 8f89be0fbd utils: add helper for adding unbounded slices to tracing spans 2024-05-06 03:45:10 -04:00
Xiretza bbdced9c90 Fix appservice namespace check for room aliases
Only normal users should be prevented from creating an alias within an
exclusive namespace, not the appservice itself. This mirrors the
behaviour in api/client_server/room.rs on room creation.
2024-05-06 03:45:10 -04:00
strawberry a6f4dc2b74 engage(lychee): check all markdown files too, enable verbose mode
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry df203fa244 add a contributing guide
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry c6e6eb0af3 ignore empty CONDUWUIT_VERSION_EXTRA for server version
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 29babebc4d adminroom: add count to list-joined-rooms user command
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
Matt Moriarity 2f3194840c fix extra version when using flake-compat 2024-05-06 03:45:10 -04:00
strawberry 0ebb323490 resolve almost all as_conversions lints
may need further opinion from others on these

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry f8e1255994 presence: set empty string status msg to None
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry b5c0c30a5e resolve half of the integer_arithmetic lints, couple misc changes
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry ac4590952b set io_uring for rocksdb a default feature
this was already enabled by default by rocksdb technically, but
it wasn't building with it properly.

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 67569cb9c8 nix: switch to fork of rocksdb input
https://github.com/girlbossceo/rocksdb/commit/db6df0b185774778457dabfcbd822cb81760cade
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 11ec0dff4f add PATCH to list of allowed HTTP methods in CORS (MSC4138)
https://github.com/matrix-org/matrix-spec-proposals/pull/4138

we already had HEAD

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry a198f0481a nix: add liburing to devshell
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 6266e0ab5e rocksdb: enable async_io if using io_uring feature
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 9ee1485960 enable overflow-checks for dev/debug profile
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 05314ec46c nix: set hardcoded NIX_OUTPATH_USED_AS_RANDOM_SEED for bindgen
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry b66d2d44d0 chore: bump MSRV to 1.77.0 as 1.78.0 came out
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 3b2db9027a envrc: allow loading env vars from .env if it exists
from https://or.computer.surgery/charles/matrix/-/commit/ffd479d66fae5092073ff380d25fac3735d055bd

This is primarily useful for replicating the environment from CI so that
the `nix-build-and-cache` script is easier to invoke.

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 97e81885db use dep: syntax in cargo.toml features
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry 706c1c993b nix: don't run cargo test for crane buildpackage
CI does this already

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-06 03:45:10 -04:00
strawberry cb70d51e2b bump conduwuit version to 0.3.2
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-05 16:52:51 -04:00
strawberry bfb827a418 send Cache-Control and CORS header for remote thumbnail responses
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-05 16:28:21 -04:00
strawberry e2fb588a8c sent attachment content-disposition on thumbnails too
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-05 16:28:21 -04:00
strawberry 43c4dfc5df set content-disposition to attachment instead of inline
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-05 16:28:21 -04:00
strawberry 42e3567153 disable overflow-checks for performance
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 02:18:24 -04:00
strawberry 75ad5cfbb7 bump conduwuit version to 0.3.1
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 02:12:34 -04:00
strawberry be5101b07c bump console-subscriber to 0.2
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 02:12:34 -04:00
strawberry c531101657 misc docs adjustments
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 761263332b ci: push to gitlab container registry too
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 5fe146aa85 docs: update differences.md
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry d7399a12fb config: split at __ for struct sections of config, add couple missing settings for show-config
this makes `CONDUWUIT_WELL_KNOWN__CLIENT` a valid env variable config
option as it would normally exist under `[well_known.client]` in toml

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 7e2a15497c use function comments for lsp here
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry e226046e15 drop default appservice_timeout to 35 seconds
AS's are generally hosted on the same machine or within the same
network

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 75b9332917 dont allow creating remote users in admin room
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry de26bf22dc adjust a couple error codes for room alias getting
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry a7c14a861b ci: output complement diff results to job summary, temp allow error
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 05b7dec482 temp(ci): comment complement results diff for now
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 38ca88da9f ci(gitlab): use --no-strip for cargo deb
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 2e5ba7ab17 ci(gitlab): use gitlab fastzip feature flag
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 35683d66dd sort the complement results by test name for consistent output
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry e1052d1829 chore: update checked-in complement test results
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 49078aa836 fix: get the presence of the requested user instead of ourselves
after getting the shared rooms with the target user, we actually only
get the presence of ourselves instead of the requested user

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry b6b739a7b7 set -vet=off to (hopefully) run all complement tests
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry fa0bdd431b add destination to X-Matrix Authorization outbound requests
we were already validating this for inbound requests

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry a6cf5cfd8b remove future deleted nix binary cache
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 37c2877cf8 chore: update checked in list of complement test results
also remove the separated passed/failed list, it's already ordered
neatly for folks to read.

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 1181a7a7a9 nix: specify explicit branches/refs for flake inputs
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry cad16b9268 ci, nix: build and cache all packages and CI dependencies
from https://or.computer.surgery/charles/matrix/-/commit/f5bd9bc45e5e5eaf76cff31f1c259ed3f39fb88a
with changes for GitHub CI and misc

Co-authored-by: Charles Hall <charles@computer.surgery>
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 3b410d0556 ci: run complement with direnv
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 28f599236a ci: compare complement results with checked-in results
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 365c85ad27 use nix-output-monitor if available
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 13f1274c35 run complement in CI (does not compare results yet)
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry c4beb7d462 dont return "Allocator" header for server memory-usage if empty
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 0f13ada300 return more user-friendly message for debug memory-stats
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry a7f8c848aa refactor and simplify room creation route a bit
removes a couple unnecessary checks, uses our room_id ruma request field

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 25bc1f069d chore: bump deps
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 0223386243 remove this unnecessary log, use debug_warn
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry a496cc4705 dedupe version getting code, rename to CONDUWUIT_VERSION_EXTRA
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 8ec9372a8e lint
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
Charles Hall a01a7e1219 improve "Leave event has no state" log
To include the user, room, and event ID.

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry db81ffb4ea nix: only set CONDUIT_VERSION_EXTRA for final build + slight cleanup
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 096c252dc2 move hierarchy via servers higher up, add some debug logging to it
this entire thing needs to be cleaned up later, but i need spaces
to work

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 1464b30433 add workaround for room creation initial_state event content as {}, slight refactor
this will simply skip over the events

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
Jason Volk 3585e8a2ef rename / simplify tester stub for now
Signed-off-by: Jason Volk <jason@zemos.net>
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
Jason Volk b19d2ad5b0 daily logging improvements
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-03 01:52:29 -04:00
Jason Volk 8ecf722abb split http serving from main.
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-03 01:52:29 -04:00
Jason Volk 5d76db8f19 add configuration for rocksdb direct-io enablement
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-03 01:52:29 -04:00
strawberry f4a2b39d55 split up alias.rs a bit (alias checks and room alias server name stuff)
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry e00b65b0e0 use ok_or_else instead of ok_or for backup.rs
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry beeacd4ef1 initialise capabilities with default constructor
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry e5735c81ed dedupe half of account/room data config.rs code
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry b17ccdadd2 dedupe some code in state.rs
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 8e3918250d rm complement test logs, rm docker healthcheck.sh, rm .vscode/ dir, move test results to tests/ dir
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 6021cb0a1f partially revert this
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 35114dde7d add query_over_tcp_only config option for hickory
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 62fd6e2c7c set AD bit to false in hickory
this is purely DNSSEC related which we don't use, and DNSSEC on matrix
is unbearable for federation (no one sets it up properly, it's extremely taxing, etc)

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 668a7645e9 add ip_lookup_strategy config option for hickory resolver
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 3f8407dd64 add hot_lib to default.nix src include
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry b8c4d6b157 bump ruma
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 0b39bb813e tiny refactoring, split out report_event_route a bit
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry d32ea6ec20 cargo doc lints
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 041a7a90f3 hot lib things again
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 9c0c4c292c document hot_lib for developers a bit
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry ed86a4aa9e slight misc adjustments
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry b282c1eb6d add (probably messy) support for hot lib reload via admin command
`!admin test test1`

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 76c5942b4f use user_is_local and server_is_ours more, remove few double filters
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry e7505a4b20 resolve ptr_as_ptr lint
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry a97520b0e9 bump MSRV to 1.76.0
there's really no point in trying to stay as low as possible for us,
and this makes development easier. Debian users should just use rustup,
Nix users already get the proper toolchains.

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 9931e60050 use single global function for server name local and user local checking
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 8f17d965b2 use <pre> for codeblock formatting in jemalloc stats, link to ffi func
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
Jason Volk 9f5d7b0761 fix mallctl suite lints
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-03 01:52:29 -04:00
Charles Hall 4faf690f57 run clippy on default, all, and allocator features
This way all 4 major configurations are linted.
2024-05-03 01:52:29 -04:00
Charles Hall 838550536a reflow clippy in engage file 2024-05-03 01:52:29 -04:00
Charles Hall 3b05417246 handle the case where 0 or >1 allocs are enabled
In particular this fixes `cargo build --all-features`.
2024-05-03 01:52:29 -04:00
Charles Hall e0c0d51a05 fix lints 2024-05-03 01:52:29 -04:00
Jason Volk e4b669360f start mallctl suite w/ jemalloc stats
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-03 01:52:29 -04:00
Jason Volk 56f652c12d cleanup admin worker loop
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-03 01:52:29 -04:00
Jason Volk 4b6938e0f6 add admin server uptime command
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-03 01:52:29 -04:00
Benjamin Lee 781d4b7907 document tracing_flame options in example config 2024-05-03 01:52:29 -04:00
Benjamin Lee 56f1e905de add config option tracing_flame_output_path
Hardcoding the output path to something in CWD is a pain if you're running
conduwuit through systemd or similar. Also made the error message when
it's unable to create the output file a little more friendly.
2024-05-03 01:52:29 -04:00
Benjamin Lee 646b31d2bd flush tracing-flame output file on exit
Previously we were dropping the flush guard early, possibly causing
samples to be lost on exit.
2024-05-03 01:52:29 -04:00
Benjamin Lee 7d92515b1d add tracing_flame_filter config option
The previous hardcoded filter `trace,h2=off` isn't appropriate in all
cases, it's better to have this be configurable.
2024-05-03 01:52:29 -04:00
Benjamin Lee cc578d9a67 keep stdout logs when tracing-flame/jaeger is enabled
Previously, enabling the `tracing_flame` or `allow_jaeger` options would
prevent any logs from being written to stdout. In addition, enabling the
`allow_jaeger` option would inhibit the `tracing_flame` option.

Now that we have a way to use separate tracing filters with different
layers, we can enable all three at the same time without issues.

This commit also prevents the `debug log_level` command from modifying
the `tracing-flame` filter. This was supported previously, but I don't
think it's something that you would ever want to do intentionally. Now
that we have both the normal log filter and the `tracing-flame` filter
enabled at the same time, we want to `debug log_level` to only modify the
normal filter.
2024-05-03 01:52:29 -04:00
strawberry bf713cd0ba lints
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 61f813c187 admin command to get rooms a remote user is in, remove unnecessary dedupe+sort
imagine this SQL query but in conduwuit:

select * from users_in_public_rooms where user_id like '%user_id%';

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 450f15df4f admin debug command to fetch a server's true destination
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 1cbf2bdc6b update dns_cache_entries example config setting
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
Jason Volk b4035bf0da increase default dns cache entries
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-03 01:52:29 -04:00
Jason Volk 37ecb4f2b9 decrease log verbosity for potentially cached NoRecordsFound
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-03 01:52:29 -04:00
Jason Volk daf4b56435 fix inherited sequential small options
Signed-off-by: Jason Volk <jason@zemos.net>
2024-05-03 01:52:29 -04:00
strawberry 799b2909ab ci: dont run registry pushes if creds are not set
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 614ef5b3a1 raise dns_min_ttl_nxdomain back to 3 days
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry cfa89b8b64 add remaining other rocksdb compression options
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 9f245281b1 never allow only 1 tokio worker or rocksdb parallelism thread (max compare)
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry d172a6883d bump some deps
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
strawberry 04afc83043 switch to my fork of tracing
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-03 01:52:29 -04:00
Benjamin Lee 8a5599adf9 add optional support for tokio-console
This turned out to be quite hairy, mostly because we need to apply the
config's log level filter to the actual logs (stdout and, optionally
sentry), but do not want to filter out the tokio tracing events needed by
the console_subscriber. I hit several edge cases in tracing getting
this to work, and we now depend on a git version of tracing with a
backported patch :(
2024-05-03 01:52:29 -04:00
282 changed files with 16241 additions and 52148 deletions
+3 -1
View File
@@ -1,5 +1,7 @@
#!/usr/bin/env bash
use flake
use flake ".#${DIRENV_DEVSHELL:-default}"
PATH_add bin
dotenv_if_exists
+135 -34
View File
@@ -14,10 +14,10 @@ on:
- 'docs/**'
- 'debian/**'
- 'docker/**'
- 'test_results/**'
branches:
- main
- dev
tags:
- '*'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
@@ -35,6 +35,11 @@ env:
# Custom nix binary cache if fork is being used
ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}
# Use the all-features devshell instead of default, to ensure that features
# match between nix and cargo
DIRENV_DEVSHELL: all-features
# Get error output from nix that we can actually use
NIX_CONFIG: show-trace = true
permissions:
packages: write
@@ -48,12 +53,24 @@ jobs:
- name: Sync repository
uses: actions/checkout@v4
- name: Tag comparison check
if: startsWith(github.ref, 'refs/tags/v')
run: |
# Tag mismatch with latest repo tag check to prevent potential downgrades
LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
if [ $LATEST_TAG != ${{ github.ref_name }} ]; then
echo '# WARNING: Attempting to run this workflow for a tag that is not the latest repo tag. Aborting.'
echo '# WARNING: Attempting to run this workflow for a tag that is not the latest repo tag. Aborting.' >> $GITHUB_STEP_SUMMARY
exit 1
fi
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
- name: Enable Cachix binary cache
run: |
nix-env -iA cachix -f https://cachix.org/api/v1/install
nix profile install nixpkgs#cachix
cachix use crane
cachix use nix-community
@@ -63,8 +80,8 @@ jobs:
- name: Apply Nix binary cache configuration
run: |
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
extra-substituters = https://nix.computer.surgery/conduit https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit
extra-trusted-public-keys = conduit:ZGAf6P6LhNvnoJJ3Me3PRg7tlLSrPxcQ2RiE5LIppjo= conduit:Isq8FGyEC6FOXH6nD+BOeAA+bKp6X6UIbupSlGEPuOg= conduwuit:lYPVh7o1hLu1idH4Xt2QHaRa49WRGSAqzcfFd94aOTw=
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o=
EOF
- name: Use alternative Nix binary caches if specified
@@ -78,14 +95,61 @@ jobs:
- name: Prepare build environment
run: |
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
nix-env -f "<nixpkgs>" -iA direnv -iA nix-direnv
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
direnv allow
nix develop --command true
nix develop .#all-features --command true
- name: Cache CI dependencies
run: |
bin/nix-build-and-cache ci
- name: Run CI tests
run: |
direnv exec . engage > >(tee -a test_output.log)
- name: Sync Complement repository
uses: actions/checkout@v4
with:
repository: 'matrix-org/complement'
path: complement_src
- name: Run Complement tests
run: |
direnv exec . bin/complement 'complement_src' 'complement_test_logs.jsonl' 'complement_test_results.jsonl'
cp -v -f result complement_oci_image.tar.gz
- name: Upload Complement OCI image
uses: actions/upload-artifact@v4
with:
name: complement_oci_image.tar.gz
path: complement_oci_image.tar.gz
if-no-files-found: error
- name: Upload Complement logs
uses: actions/upload-artifact@v4
with:
name: complement_test_logs.jsonl
path: complement_test_logs.jsonl
if-no-files-found: error
- name: Upload Complement results
uses: actions/upload-artifact@v4
with:
name: complement_test_results.jsonl
path: complement_test_results.jsonl
if-no-files-found: error
- name: Diff Complement results with checked-in repo results
run: |
diff -u --color=always tests/test_results/complement/test_results.jsonl complement_test_results.jsonl > >(tee -a complement_test_output.log)
- name: Add Complement diff result to Job Summary
run: |
echo '# Complement diff results' >> $GITHUB_STEP_SUMMARY
echo '```diff' >> $GITHUB_STEP_SUMMARY
tail -n 100 complement_test_output.log | sed 's/\x1b\[[0-9;]*m//g' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
- name: Update Job Summary
if: success() || failure()
run: |
@@ -101,14 +165,12 @@ jobs:
name: Build
runs-on: ubuntu-latest
needs: tests
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev'
if: github.event.pull_request.draft != true
strategy:
matrix:
include:
- target: aarch64-unknown-linux-musl
- target: aarch64-unknown-linux-musl-jemalloc
- target: x86_64-unknown-linux-musl
- target: x86_64-unknown-linux-musl-jemalloc
steps:
- name: Sync repository
uses: actions/checkout@v4
@@ -116,9 +178,9 @@ jobs:
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
- name: Enable Cachix binary cache
- name: Install and enable Cachix binary cache
run: |
nix-env -iA cachix -f https://cachix.org/api/v1/install
nix profile install nixpkgs#cachix
cachix use crane
cachix use nix-community
@@ -128,8 +190,8 @@ jobs:
- name: Apply Nix binary cache configuration
run: |
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
extra-substituters = https://nix.computer.surgery/conduit https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit
extra-trusted-public-keys = conduit:ZGAf6P6LhNvnoJJ3Me3PRg7tlLSrPxcQ2RiE5LIppjo= conduit:Isq8FGyEC6FOXH6nD+BOeAA+bKp6X6UIbupSlGEPuOg= conduwuit:lYPVh7o1hLu1idH4Xt2QHaRa49WRGSAqzcfFd94aOTw=
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o=
EOF
- name: Use alternative Nix binary caches if specified
@@ -143,17 +205,23 @@ jobs:
- name: Prepare build environment
run: |
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
nix-env -f "<nixpkgs>" -iA direnv -iA nix-direnv
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
direnv allow
nix develop --command true
nix develop .#all-features --command true
- name: Build static ${{ matrix.target }}
run: |
bin/nix-build-and-cache .#static-${{ matrix.target }}
mkdir -p target/release
cp -v -f result/bin/conduit target/release/
direnv exec . cargo deb --no-build --no-strip --output target/debian/${{ matrix.target }}.deb
mv target/release/conduit static-${{ matrix.target }}
CARGO_DEB_TARGET_TUPLE=$(echo ${{ matrix.target }} | grep -o -E '^([^-]*-){3}[^-]*')
bin/nix-build-and-cache just .#static-${{ matrix.target }}
mkdir -v -p target/release/
mkdir -v -p target/$CARGO_DEB_TARGET_TUPLE/release/
cp -v -f result/bin/conduit target/release/conduwuit
cp -v -f result/bin/conduit target/$CARGO_DEB_TARGET_TUPLE/release/conduwuit
# -p conduit is the main crate name
direnv exec . cargo deb --verbose --no-build --no-strip -p conduit --target=$CARGO_DEB_TARGET_TUPLE --output target/release/${{ matrix.target }}.deb
mv -v target/release/conduwuit static-${{ matrix.target }}
mv -v target/release/${{ matrix.target }}.deb ${{ matrix.target }}.deb
- name: Upload static-${{ matrix.target }}
uses: actions/upload-artifact@v4
@@ -166,12 +234,13 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: deb-${{ matrix.target }}
path: target/debian/${{ matrix.target }}.deb
path: ${{ matrix.target }}.deb
if-no-files-found: error
compression-level: 0
- name: Build OCI image ${{ matrix.target }}
run: |
bin/nix-build-and-cache .#oci-image-${{ matrix.target }}
bin/nix-build-and-cache just .#oci-image-${{ matrix.target }}
cp -v -f result oci-image-${{ matrix.target }}.tar.gz
- name: Upload OCI image ${{ matrix.target }}
@@ -186,16 +255,23 @@ jobs:
name: Docker publish
runs-on: ubuntu-latest
needs: build
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev') && github.event_name != 'pull_request'
if: (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event.pull_request.draft != true)) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '')
env:
DOCKER_ARM64: docker.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}-arm64v8
DOCKER_AMD64: docker.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}-amd64
DOCKER_TAG: docker.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}
DOCKER_BRANCH: docker.io/${{ github.repository }}:${{ (github.ref == 'refs/heads/main' && 'latest') || github.ref_name }}
GHCR_ARM64: ghcr.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}-arm64v8
GHCR_AMD64: ghcr.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}-amd64
GHCR_TAG: ghcr.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}
GHCR_BRANCH: ghcr.io/${{ github.repository }}:${{ (github.ref == 'refs/heads/main' && 'latest') || github.ref_name }}
DOCKER_ARM64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
DOCKER_AMD64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
DOCKER_TAG: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
DOCKER_BRANCH: docker.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
GHCR_ARM64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
GHCR_AMD64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
GHCR_TAG: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
GHCR_BRANCH: ghcr.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
GLCR_ARM64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
GLCR_AMD64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
GLCR_TAG: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
GLCR_BRANCH: registry.gitlab.com/conduwuit/conduwuit:${{ (startsWith(github.ref, 'refs/tags/v') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }}
steps:
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
@@ -205,51 +281,76 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Docker Hub
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
uses: docker/login-action@v3
with:
registry: docker.io
username: ${{ vars.DOCKER_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitLab Container Registry
if: ${{ (vars.GITLAB_USERNAME != '') && (env.GITLAB_TOKEN != '') }}
uses: docker/login-action@v3
with:
registry: registry.gitlab.com
username: ${{ vars.GITLAB_USERNAME }}
password: ${{ secrets.GITLAB_TOKEN }}
- name: Download artifacts
uses: actions/download-artifact@v4
- name: Move OCI images into position
run: |
mv oci-image-x86_64-*-jemalloc/*.tar.gz oci-image-amd64.tar.gz
mv oci-image-aarch64-*-jemalloc/*.tar.gz oci-image-arm64v8.tar.gz
mv -v oci-image-x86_64-*/*.tar.gz oci-image-amd64.tar.gz
mv -v oci-image-aarch64-*/*.tar.gz oci-image-arm64v8.tar.gz
- name: Load and push amd64 image
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
run: |
docker load -i oci-image-amd64.tar.gz
docker tag $(docker images -q conduit:main) ${{ env.DOCKER_AMD64 }}
docker tag $(docker images -q conduit:main) ${{ env.GHCR_AMD64 }}
docker tag $(docker images -q conduit:main) ${{ env.GLCR_AMD64 }}
docker push ${{ env.DOCKER_AMD64 }}
docker push ${{ env.GHCR_AMD64 }}
docker push ${{ env.GLCR_AMD64 }}
- name: Load and push arm64 image
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
run: |
docker load -i oci-image-arm64v8.tar.gz
docker tag $(docker images -q conduit:main) ${{ env.DOCKER_ARM64 }}
docker tag $(docker images -q conduit:main) ${{ env.GHCR_ARM64 }}
docker tag $(docker images -q conduit:main) ${{ env.GLCR_ARM64 }}
docker push ${{ env.DOCKER_ARM64 }}
docker push ${{ env.GHCR_ARM64 }}
docker push ${{ env.GLCR_ARM64 }}
- name: Create Docker combined manifests
run: |
# Dockerhub Container Registry
docker manifest create ${{ env.DOCKER_TAG }} --amend ${{ env.DOCKER_ARM64 }} --amend ${{ env.DOCKER_AMD64 }}
docker manifest create ${{ env.DOCKER_BRANCH }} --amend ${{ env.DOCKER_ARM64 }} --amend ${{ env.DOCKER_AMD64 }}
# GitHub Container Registry
docker manifest create ${{ env.GHCR_TAG }} --amend ${{ env.GHCR_ARM64 }} --amend ${{ env.GHCR_AMD64 }}
docker manifest create ${{ env.GHCR_BRANCH }} --amend ${{ env.GHCR_ARM64 }} --amend ${{ env.GHCR_AMD64 }}
# GitLab Container Registry
docker manifest create ${{ env.GLCR_TAG }} --amend ${{ env.GLCR_ARM64 }} --amend ${{ env.GCCR_AMD64 }}
docker manifest create ${{ env.GLCR_BRANCH }} --amend ${{ env.GLCR_ARM64 }} --amend ${{ env.GLCR_AMD64 }}
- name: Push manifests to Docker registries
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
run: |
docker manifest push ${{ env.DOCKER_TAG }}
docker manifest push ${{ env.DOCKER_BRANCH }}
docker manifest push ${{ env.GHCR_TAG }}
docker manifest push ${{ env.GHCR_BRANCH }}
docker manifest push ${{ env.GLCR_TAG }}
docker manifest push ${{ env.GLCR_BRANCH }}
- name: Add Image Links to Job Summary
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
run: |
echo "- \`docker pull ${{ env.DOCKER_TAG }}\`" >> $GITHUB_STEP_SUMMARY
echo "- \`docker pull ${{ env.GHCR_TAG }}\`" >> $GITHUB_STEP_SUMMARY
echo "- \`docker pull ${{ env.GLCR_TAG }}\`" >> $GITHUB_STEP_SUMMARY
+8 -8
View File
@@ -5,6 +5,8 @@ on:
push:
branches:
- main
tags:
- '*'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
@@ -47,7 +49,7 @@ jobs:
uses: actions/configure-pages@v5
- name: Install Nix (with flakes and nix-command enabled)
uses: cachix/install-nix-action@v26
uses: cachix/install-nix-action@v27
with:
nix_path: nixpkgs=channel:nixos-unstable
@@ -58,12 +60,10 @@ jobs:
extra-trusted-public-keys = nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=
extra-substituters = https://crane.cachix.org
extra-trusted-public-keys = crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=
extra-substituters = https://nix.computer.surgery/conduit
extra-trusted-public-keys = conduit:ZGAf6P6LhNvnoJJ3Me3PRg7tlLSrPxcQ2RiE5LIppjo=
extra-substituters = https://attic.kennel.juneis.dog/conduit
extra-trusted-public-keys = conduit:Isq8FGyEC6FOXH6nD+BOeAA+bKp6X6UIbupSlGEPuOg=
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk=
extra-substituters = https://attic.kennel.juneis.dog/conduwuit
extra-trusted-public-keys = conduwuit:lYPVh7o1hLu1idH4Xt2QHaRa49WRGSAqzcfFd94aOTw=
extra-trusted-public-keys = conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE=
- name: Add alternative Nix binary caches if specified
if: ${{ (env.ATTIC_ENDPOINT != '') && (env.ATTIC_PUBLIC_KEY != '') }}
@@ -88,13 +88,13 @@ jobs:
- name: Allow direnv
run: direnv allow
- name: Cache x86_64 inputs for devShell
- name: Cache CI dependencies
run: |
./bin/nix-build-and-cache .#devShells.x86_64-linux.default.inputDerivation
./bin/nix-build-and-cache ci
- name: Build documentation (book)
run: |
./bin/nix-build-and-cache .#book
./bin/nix-build-and-cache just .#book
cp -r --dereference result public
- name: Upload generated documentation (book) as normal artifact
uses: actions/upload-artifact@v4
+5 -3
View File
@@ -5,6 +5,8 @@ on:
push:
branches:
- main
tags:
- '*'
schedule:
- cron: '00 12 * * *'
@@ -24,7 +26,7 @@ jobs:
uses: actions/checkout@v4
- name: Run Trivy code and vulnerability scanner on repo
uses: aquasecurity/trivy-action@0.19.0
uses: aquasecurity/trivy-action@0.21.0
with:
scan-type: repo
format: sarif
@@ -32,9 +34,9 @@ jobs:
severity: CRITICAL,HIGH,MEDIUM,LOW
- name: Run Trivy code and vulnerability scanner on filesystem
uses: aquasecurity/trivy-action@0.19.0
uses: aquasecurity/trivy-action@0.21.0
with:
scan-type: fs
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH,MEDIUM,LOW
severity: CRITICAL,HIGH,MEDIUM,LOW
+9
View File
@@ -1,3 +1,6 @@
# Local environment overrides
/.env
# CMake
cmake-build-*/
@@ -81,8 +84,14 @@ public/
# macOS
.DS_Store
# VS Code
.vscode/
# Zed
.zed/
# idk where you're coming from, but i'm tired of you
rustc-ice-*
# complement test logs are huge
tests/test_results/complement/test_logs.jsonl
+20 -59
View File
@@ -6,6 +6,10 @@ stages:
variables:
# Makes some things print in color
TERM: ansi
# Faster cache and artifact compression / decompression
FF_USE_FASTZIP: true
# Print progress reports for cache and artifact transfers
TRANSFER_METER_FREQUENCY: 5s
# Avoid duplicate pipelines
# See: https://docs.gitlab.com/ee/ci/yaml/workflow.html#switch-between-branch-pipelines-and-merge-request-pipelines
@@ -22,19 +26,19 @@ before_script:
# Add conduwuit binary cache
- if command -v nix > /dev/null; then echo "extra-substituters = https://attic.kennel.juneis.dog/conduwuit" >> /etc/nix/nix.conf; fi
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = conduwuit:lYPVh7o1hLu1idH4Xt2QHaRa49WRGSAqzcfFd94aOTw=" >> /etc/nix/nix.conf; fi
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE=" >> /etc/nix/nix.conf; fi
- if command -v nix > /dev/null; then echo "extra-substituters = https://attic.kennel.juneis.dog/conduit" >> /etc/nix/nix.conf; fi
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = conduit:Isq8FGyEC6FOXH6nD+BOeAA+bKp6X6UIbupSlGEPuOg=" >> /etc/nix/nix.conf; fi
# Add upstream Conduit binary cache
- if command -v nix > /dev/null; then echo "extra-substituters = https://nix.computer.surgery/conduit" >> /etc/nix/nix.conf; fi
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = conduit:ZGAf6P6LhNvnoJJ3Me3PRg7tlLSrPxcQ2RiE5LIppjo=" >> /etc/nix/nix.conf; fi
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk=" >> /etc/nix/nix.conf; fi
# Add alternate binary cache
- if command -v nix > /dev/null && [ -n "$ATTIC_ENDPOINT" ]; then echo "extra-substituters = $ATTIC_ENDPOINT" >> /etc/nix/nix.conf; fi
- if command -v nix > /dev/null && [ -n "$ATTIC_PUBLIC_KEY" ]; then echo "extra-trusted-public-keys = $ATTIC_PUBLIC_KEY" >> /etc/nix/nix.conf; fi
# Add Lix binary cache
- if command -v nix > /dev/null; then echo "extra-substituters = https://cache.lix.systems" >> /etc/nix/nix.conf; fi
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o=" >> /etc/nix/nix.conf; fi
# Add crane binary cache
- if command -v nix > /dev/null; then echo "extra-substituters = https://crane.cachix.org" >> /etc/nix/nix.conf; fi
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=" >> /etc/nix/nix.conf; fi
@@ -54,10 +58,10 @@ before_script:
ci:
stage: ci
image: nixos/nix:2.22.0
image: nixos/nix:2.22.1
script:
# Cache the inputs required for the devShell
- ./bin/nix-build-and-cache .#devShells.x86_64-linux.default.inputDerivation
# Cache CI dependencies
- ./bin/nix-build-and-cache ci
- direnv exec . engage
cache:
@@ -79,14 +83,14 @@ ci:
artifacts:
stage: artifacts
image: nixos/nix:2.22.0
image: nixos/nix:2.22.1
script:
- ./bin/nix-build-and-cache .#static-x86_64-unknown-linux-musl
- ./bin/nix-build-and-cache just .#static-x86_64-unknown-linux-musl
- cp result/bin/conduit x86_64-unknown-linux-musl
- mkdir -p target/release
- cp result/bin/conduit target/release
- direnv exec . cargo deb --no-build
- direnv exec . cargo deb --no-build --no-strip
- mv target/debian/*.deb x86_64-unknown-linux-musl.deb
# Since the OCI image package is based on the binary package, this has the
@@ -97,16 +101,16 @@ artifacts:
# Note that although we have an `oci-image-x86_64-unknown-linux-musl`
# output, we don't build it because it would be largely redundant to this
# one since it's all containerized anyway.
- ./bin/nix-build-and-cache .#oci-image
- ./bin/nix-build-and-cache just .#oci-image
- cp result oci-image-amd64.tar.gz
- ./bin/nix-build-and-cache .#static-aarch64-unknown-linux-musl
- ./bin/nix-build-and-cache just .#static-aarch64-unknown-linux-musl
- cp result/bin/conduit aarch64-unknown-linux-musl
- ./bin/nix-build-and-cache .#oci-image-aarch64-unknown-linux-musl
- ./bin/nix-build-and-cache just .#oci-image-aarch64-unknown-linux-musl
- cp result oci-image-arm64v8.tar.gz
- ./bin/nix-build-and-cache .#book
- ./bin/nix-build-and-cache just .#book
# We can't just copy the symlink, we need to dereference it https://gitlab.com/gitlab-org/gitlab/-/issues/19746
- cp -r --dereference result public
artifacts:
@@ -127,49 +131,6 @@ artifacts:
- if: $CI
interruptible: true
.push-oci-image:
stage: publish
image: docker:26.0.2
services:
- docker:26.0.2-dind
variables:
IMAGE_SUFFIX_AMD64: amd64
IMAGE_SUFFIX_ARM64V8: arm64v8
script:
- docker load -i oci-image-amd64.tar.gz
- IMAGE_ID_AMD64=$(docker images -q conduit:main)
- docker load -i oci-image-arm64v8.tar.gz
- IMAGE_ID_ARM64V8=$(docker images -q conduit:main)
# Tag and push the architecture specific images
- docker tag $IMAGE_ID_AMD64 $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64
- docker tag $IMAGE_ID_ARM64V8 $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8
- docker push $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64
- docker push $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8
# Tag the multi-arch image
- docker manifest create $IMAGE_NAME:$CI_COMMIT_SHA --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8
- docker manifest push $IMAGE_NAME:$CI_COMMIT_SHA
# Tag and push the git ref
- docker manifest create $IMAGE_NAME:$CI_COMMIT_REF_NAME --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8
- docker manifest push $IMAGE_NAME:$CI_COMMIT_REF_NAME
# Tag git tags as 'latest'
- |
if [[ -n "$CI_COMMIT_TAG" ]]; then
docker manifest create $IMAGE_NAME:latest --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8
docker manifest push $IMAGE_NAME:latest
fi
dependencies:
- artifacts
only:
- main
- tags
oci-image:push-gitlab:
extends: .push-oci-image
variables:
IMAGE_NAME: $CI_REGISTRY_IMAGE/conduwuit
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
pages:
stage: publish
dependencies:
-11
View File
@@ -1,11 +0,0 @@
{
"recommendations": [
"rust-lang.rust-analyzer",
"editorconfig.editorconfig",
"ms-azuretools.vscode-docker",
"eamodio.gitlens",
"serayuzgur.crates",
"vadimcn.vscode-lldb",
"timonwong.shellcheck"
]
}
-35
View File
@@ -1,35 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug conduit",
"sourceLanguages": ["rust"],
"cargo": {
"args": [
"build",
"--bin=conduit",
"--package=conduit"
],
"filter": {
"name": "conduit",
"kind": "bin"
}
},
"args": [],
"env": {
"RUST_BACKTRACE": "1",
"CONDUIT_DATABASE_PATH": "/tmp/awawawa",
"CONDUIT_ADDRESS": "0.0.0.0",
"CONDUIT_PORT": "55551",
"CONDUIT_SERVER_NAME": "your.server.name",
"CONDUIT_LOG": "debug"
},
"cwd": "${workspaceFolder}"
}
]
}
+92
View File
@@ -0,0 +1,92 @@
# Contributing guide
This page is for about contributing to conduwuit. The [development](docs/development.md) page may be of interest for you as well.
If you would like to work on an [issue][issues] that is not assigned, preferably ask in the Matrix room first at [#conduwuit:puppygock.gay][conduwuit-matrix], and comment on it.
### Linting and Formatting
It is mandatory all your changes satisfy the lints (clippy, rustc, rustdoc, etc) and your code is formatted via the **nightly** `cargo fmt`. A lot of the `rustfmt.toml` features depend on nightly toolchain. It would be ideal if they weren't nightly-exclusive features, but they currently still are. CI's rustfmt uses nightly.
If you need to allow a lint, please make sure it's either obvious as to why (e.g. clippy saying redundant clone but it's actually required) or it has a comment saying why. Do not write inefficient code for the sake of satisfying lints. If a lint is wrong and provides a more inefficient solution or suggestion, allow the lint and mention that in a comment.
### Running CI tests locally
conduwuit's CI for tests, linting, formatting, audit, etc use [`engage`][engage]. engage can be installed from nixpkgs or `cargo install engage`. conduwuit's Nix flake devshell has the nixpkgs engage with `direnv`. Use `engage --help` for more usage details.
To test, format, lint, etc that CI would do, install engage, allow the `.envrc` file using `direnv allow`, and run `engage`.
All of the tasks are defined at the [engage.toml][engage.toml] file. You can view all of them neatly by running `engage list`
If you would like to run only a specific engage task group, use `just`:
- `engage just <group>`
- Example: `engage just lints`
If you would like to run a specific engage task in a specific group, use `just <GROUP> [TASK]`: `engage just lints cargo-fmt`
The following binaries are used in [`engage.toml`][engage.toml]:
- [`engage`][engage]
- `nix`
- [`direnv`][direnv]
- `rustc`
- `cargo`
- `cargo-fmt`
- `rustdoc`
- `cargo-clippy`
- [`cargo-audit`][cargo-audit]
- [`cargo-deb`][cargo-deb]
- [`lychee`][lychee]
### Matrix tests
CI runs [Complement][complement], but currently does not fail if results from the checked-in results differ with the new results. If your changes are done to fix Matrix tests, note that in your pull request. If more Complement tests start failing from your changes, please review the logs (they are uploaded as artifacts) and determine if they're intended or not.
If you'd like to run Complement locally using Nix, see the [testing](docs/development/testing.md) page.
[Sytest][sytest] support will come soon.
### Writing documentation
conduwuit's website uses [`mdbook`][mdbook] and deployed via CI using GitHub Pages in the [`documentation.yml`][documentation.yml] workflow file with Nix's mdbook in the devshell. All documentation is in the `docs/` directory at the top level. The compiled mdbook website is also uploaded as an artifact.
To build the documentation using Nix, run: `bin/nix-build-and-cache just .#book`
The output of the mdbook generation is in `result/`. mdbooks can be opened in your browser from the individual HTML files without any web server needed.
### Inclusivity and Diversity
All **MUST** code and write with inclusivity and diversity in mind. See the [following page by Google on writing inclusive code and documentation](https://developers.google.com/style/inclusive-documentation).
This **EXPLICITLY** forbids usage of terms like "blacklist"/"whitelist" and "master"/"slave", [forbids gender-specific words and phrases](https://developers.google.com/style/pronouns#gender-neutral-pronouns), forbids ableist language like "sanity-check", "cripple", or "insane", and forbids culture-specific language (e.g. US-only holidays or cultures).
No exceptions are allowed. Dependencies that may use these terms are allowed but [do not replicate the name in your functions or variables](https://developers.google.com/style/inclusive-documentation#write-around).
In addition to language, write and code with the user experience in mind. This is software that intends to be used by everyone, so make it easy and comfortable for everyone to use. 🏳️‍⚧️
### Variable, comment, function, etc standards
Rust's default style and standards with regards to [function names, variable names, comments](https://rust-lang.github.io/api-guidelines/naming.html), etc applies here.
### Creating pull requests
Please try to keep contributions to the GitHub. While the mirrors of conduwuit allow for pull/merge requests, there is no guarantee I will see them in a timely manner. Additionally, please mark WIP or unfinished or incomplete PRs as drafts. This prevents me from having to ping once in a while to double check the status of it, especially when the CI completed successfully and everything so it *looks* done.
If you open a pull request on one of the mirrors, it is your responsibility to inform me about its existence. In the future I may try to solve this with more repo bots in the conduwuit Matrix room. There is no mailing list or email-patch support on the sr.ht mirror, but if you'd like to email me a git patch you can do so at `strawberry@puppygock.gay`.
Direct all PRs/MRs to the `main` branch.
By sending a pull request or patch, you are agreeing that your changes are allowed to be licenced under the Apache-2.0 licence and all of your conduct is in line with the Contributor's Covenant.
[issues]: https://github.com/girlbossceo/conduwuit/issues
[conduwuit-matrix]: https://matrix.to/#/#conduwuit:puppygock.gay
[complement]: https://github.com/matrix-org/complement/
[engage.toml]: https://github.com/girlbossceo/conduwuit/blob/main/engage.toml
[engage]: https://charles.page.computer.surgery/engage/
[sytest]: https://github.com/matrix-org/sytest/
[cargo-deb]: https://github.com/kornelski/cargo-deb
[lychee]: https://github.com/lycheeverse/lychee
[cargo-audit]: https://github.com/RustSec/rustsec/tree/main/cargo-audit
[direnv]: https://direnv.net/
[mdbook]: https://rust-lang.github.io/mdBook/
[documentation.yml]: https://github.com/girlbossceo/conduwuit/blob/main/.github/workflows/documentation.yml
Generated
+867 -592
View File
File diff suppressed because it is too large Load Diff
+489 -338
View File
File diff suppressed because it is too large Load Diff
+6 -11
View File
@@ -2,8 +2,6 @@
`main` / stable: [![CI and Artifacts](https://github.com/girlbossceo/conduwuit/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/girlbossceo/conduwuit/actions/workflows/ci.yml)
`dev`: [![CI and Artifacts](https://github.com/girlbossceo/conduwuit/actions/workflows/ci.yml/badge.svg?branch=dev)](https://github.com/girlbossceo/conduwuit/actions/workflows/ci.yml)
<!-- ANCHOR: catchphrase -->
### a very cool, featureful fork of [Conduit](https://conduit.rs/)
<!-- ANCHOR_END: catchphrase -->
@@ -28,6 +26,10 @@ friends or company.
An official conduwuit server ran by me is available at transfem.dev ([element.transfem.dev](https://element.transfem.dev) / [cinny.transfem.dev](https://cinny.transfem.dev))
transfem.dev is a public homeserver that can be used, it is not a "test only homeserver". This means there are rules, so please read the rules: [https://transfem.dev/homeserver_rules.txt](https://transfem.dev/homeserver_rules.txt)
transfem.dev is also listed at [servers.joinmatrix.org](https://servers.joinmatrix.org/)
#### What is the current status?
conduwuit is a hard fork of Conduit which is in beta, meaning you can join and participate in most
@@ -37,14 +39,6 @@ from time to time.
<!-- ANCHOR_END: body -->
<!-- ANCHOR: footer -->
#### How can I contribute?
1. Look for an issue you would like to work on and make sure it's not assigned
to other users
2. Ask someone to assign the issue to you (comment on the issue or chat in
[#conduwuit:puppygock.gay](https://matrix.to/#/#conduwuit:puppygock.gay))
3. Fork the repo and work on the issue.
4. Submit a PR (please keep contributions to the GitHub repo, main development is done here, not the GitLab repo which exists just as a mirror. If you are avoiding GitHub, feel free to join our Matrix chat to get your patch in.)
#### Contact
@@ -69,7 +63,8 @@ Both, but I prefer conduwuit.
#### Mirrors of conduwuit
- GitHub: <https://github.com/girlbossceo/conduwuit>
- GitLab: <https://gitlab.com/girlbossceo/conduwuit>
- GitLab: <https://gitlab.com/conduwuit/conduwuit>
- git.girlcock.ceo: <https://git.girlcock.ceo/strawberry/conduwuit>
- git.gay: <https://git.gay/june/conduwuit>
- Codeberg: <https://codeberg.org/girlbossceo/conduwuit>
- sourcehut: <https://git.sr.ht/~girlbossceo/conduwuit>
-2
View File
@@ -1,2 +0,0 @@
[advisories]
ignore = ["RUSTSEC-2020-0016"]
+17 -7
View File
@@ -15,10 +15,20 @@ LOG_FILE="$2"
# A `.jsonl` file to write test results to
RESULTS_FILE="$3"
OCI_IMAGE="complement-conduit:dev"
OCI_IMAGE="complement-conduit:main"
# Complement tests that are skipped due to flakiness/reliability issues (likely
# Complement itself induced based on various open issues)
#
# According to Go docs, these are separated by forward slashes and not pipes (why)
SKIPPED_COMPLEMENT_TESTS='-skip=TestJumpToDateEndpoint.*|TestJoinFederatedRoomFromApplicationServiceBridgeUser.*|TestFederationRoomsInvite.*|TestClientSpacesSummary.*'
toplevel="$(git rev-parse --show-toplevel)"
pushd "$toplevel" > /dev/null
bin/nix-build-and-cache just .#static-complement
pushd "$(git rev-parse --show-toplevel)" > /dev/null
nix build .#complement
docker load < result
popd > /dev/null
@@ -27,13 +37,13 @@ set +o pipefail
env \
-C "$COMPLEMENT_SRC" \
COMPLEMENT_BASE_IMAGE="$OCI_IMAGE" \
go test -timeout 1h -json ./tests | tee "$LOG_FILE"
go test -tags="conduwuit_blacklist" "$SKIPPED_COMPLEMENT_TESTS" -v -timeout 1h -json ./tests | tee "$LOG_FILE"
set -o pipefail
# Post-process the results into an easy-to-compare format
cat "$LOG_FILE" | jq -c '
# 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 '
select(
(.Action == "pass" or .Action == "fail" or .Action == "skip")
and .Test != null
) | {Action: .Action, Test: .Test}
' | sort > "$RESULTS_FILE"
' > "$RESULTS_FILE"
+78 -23
View File
@@ -2,40 +2,95 @@
set -eo pipefail
# The first argument must be the desired installable
INSTALLABLE="$1"
toplevel="$(git rev-parse --show-toplevel)"
# Build the installable and forward any other arguments too
nix build -L "$@"
# Build just the single installable and forward any other arguments too
just() {
# uses nix-output-monitor (nom) if available
if command -v nom &> /dev/null; then
nom build "$@"
else
nix build -L "$@"
fi
if [ ! -z "$ATTIC_TOKEN" ]; then
nix run --inputs-from . attic -- \
if [ -z "$ATTIC_TOKEN" ]; then
echo "\$ATTIC_TOKEN is unset, skipping uploading to the binary cache"
return
fi
# historical "conduit" store for compatibility purposes, same as conduwuit
nix run --inputs-from "$toplevel" attic -- \
login \
conduit \
"${ATTIC_ENDPOINT:-https://attic.kennel.juneis.dog/conduit}" \
"$ATTIC_TOKEN"
# Push the target installable and its build dependencies
nix run --inputs-from . attic -- \
push \
conduit \
"$(nix path-info "$INSTALLABLE" --derivation)" \
"$(nix path-info "$INSTALLABLE")"
# Find all output paths of the installables and their build dependencies
readarray -t derivations < <(nix path-info --derivation "$@")
cache=()
for derivation in "${derivations[@]}"; do
cache+=(
"$(nix-store --query --requisites --include-outputs "$derivation")"
)
done
# Upload them to Attic (conduit store)
#
# Use `xargs` and a here-string because something would probably explode if
# several thousand arguments got passed to a command at once. Hopefully no
# store paths include a newline in them.
(
IFS=$'\n'
nix shell --inputs-from "$toplevel" attic -c xargs \
attic push conduit <<< "${cache[*]}"
)
# push to "conduwuit" too
nix run --inputs-from . attic -- \
# main "conduwuit" store
nix run --inputs-from "$toplevel" attic -- \
login \
conduwuit \
"${ATTIC_ENDPOINT:-https://attic.kennel.juneis.dog/conduwuit}" \
"$ATTIC_TOKEN"
# Push the target installable and its build dependencies
nix run --inputs-from . attic -- \
push \
conduwuit \
"$(nix path-info "$INSTALLABLE" --derivation)" \
"$(nix path-info "$INSTALLABLE")"
else
echo "\$ATTIC_TOKEN is unset, skipping uploading to the binary cache"
fi
# Upload them to Attic (conduwuit store)
#
# Use `xargs` and a here-string because something would probably explode if
# several thousand arguments got passed to a command at once. Hopefully no
# store paths include a newline in them.
(
IFS=$'\n'
nix shell --inputs-from "$toplevel" attic -c xargs \
attic push conduwuit <<< "${cache[*]}"
)
}
# Build and cache things needed for CI
ci() {
cache=(
--inputs-from "$toplevel"
# Keep sorted
"$toplevel#devShells.x86_64-linux.all-features"
attic#default
nixpkgs#direnv
nixpkgs#jq
nixpkgs#nix-direnv
)
just "${cache[@]}"
}
# Build and cache *all* the package outputs from the flake.nix
packages() {
declare -a cache="($(
nix flake show --json 2> /dev/null |
nix run --inputs-from "$toplevel" nixpkgs#jq -- \
-r \
'.packages."x86_64-linux" | keys | map("'"$toplevel"'#" + .) | @sh'
))"
just "${cache[@]}"
}
eval "$@"
+62 -7
View File
@@ -60,8 +60,9 @@
### Database configuration
# This is the only directory where conduwuit will save its data, including media
database_path = "/var/lib/matrix-conduit/"
# This is the only directory where conduwuit will save its data, including media.
# Note: this was previously "/var/lib/matrix-conduit"
database_path = "/var/lib/conduwuit"
# Database backend: Only rocksdb and sqlite are supported. Please note that sqlite
# will perform significantly worse than rocksdb as it is not intended to be used the
@@ -269,6 +270,19 @@ url_preview_check_root_domain = false
# Defaults to true
allow_profile_lookup_federation_requests = true
# Config option to automatically deactivate the account of any user who attempts to join a:
# - banned room
# - forbidden room alias
# - room alias or ID with a forbidden server name
#
# This may be useful if all your banned lists consist of toxic rooms or servers that no good faith user would ever attempt to join, and
# to automatically remediate the problem without any admin user intervention.
#
# This will also make the user leave all rooms. Federation (e.g. remote room invites) are ignored here.
#
# Defaults to false as rooms can be banned for non-moderation-related reasons
#auto_deactivate_banned_room_attempts = false
### Misc
@@ -336,6 +350,18 @@ allow_profile_lookup_federation_requests = true
# messages without any attempt at redelivery.
#startup_netburst_keep = 50
# If the 'perf_measurements' feature is enabled, enables collecting folded stack trace profile of tracing spans using
# tracing_flame. The resulting profile can be visualized with inferno[1], speedscope[2], or a number of other tools.
# [1]: https://github.com/jonhoo/inferno
# [2]: www.speedscope.app
# tracing_flame = false
# If 'tracing_flame' is enabled, sets a filter for which events will be included in the profile.
# Supported syntax is documented at https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives
# tracing_flame_filter = "trace,h2=off"
# If 'tracing_flame' is enabled, set the path to write the generated profile.
# tracing_flame_output_path = "./tracing.folded"
### Generic database options
@@ -372,6 +398,10 @@ allow_profile_lookup_federation_requests = true
# Defaults to false
#rocksdb_optimize_for_spinning_disks = false
# Enables direct-io to increase database performance. This is enabled by default. Set this option to false if the
# database resides on a filesystem which does not support direct-io.
#rocksdb_direct_io = true
# RocksDB log level. This is not the same as conduwuit's log level. This is the log level for the RocksDB engine/library
# which show up in your database folder/path as `LOG` files. Defaults to error. conduwuit will typically log RocksDB errors as normal.
#rocksdb_log_level = "error"
@@ -400,11 +430,13 @@ allow_profile_lookup_federation_requests = true
#rocksdb_max_log_files = 3
# Type of RocksDB database compression to use.
# Available options are "zstd", "zlib", "bz2" and "lz4"
# Available options are "zstd", "zlib", "bz2", "lz4", or "none"
# It is best to use ZSTD as an overall good balance between speed/performance, storage, IO amplification, and CPU usage.
# For more performance but less compression (more storage used) and less CPU usage, use LZ4.
# See https://github.com/facebook/rocksdb/wiki/Compression for more details.
#
# "none" will disable compression.
#
# Defaults to "zstd"
#rocksdb_compression_algo = "zstd"
@@ -471,7 +503,7 @@ allow_profile_lookup_federation_requests = true
# Maximum entries stored in DNS memory-cache. The size of an entry may vary so please take care if
# raising this value excessively. Only decrease this when using an external DNS cache. Please note
# that systemd does *not* count as an external cache, even when configured to do so.
#dns_cache_entries = 12288
#dns_cache_entries = 32768
# Minimum time-to-live in seconds for entries in the DNS cache. The default may appear high to most
# administrators; this is by design. Only decrease this if you are using an external DNS cache.
@@ -480,7 +512,9 @@ allow_profile_lookup_federation_requests = true
# Minimum time-to-live in seconds for NXDOMAIN entries in the DNS cache. This value is critical for
# the server to federate efficiently. NXDOMAIN's are assumed to not be returning to the federation
# and aggressively cached rather than constantly rechecked.
#dns_min_ttl_nxdomain = 86400
#
# Defaults to 3 days as these are *very rarely* false negatives.
#dns_min_ttl_nxdomain = 259200
# The number of seconds to wait for a reply to a DNS query. Please note that recursive queries can
# take up to several seconds for some domains, so this value should not be too low.
@@ -498,6 +532,27 @@ allow_profile_lookup_federation_requests = true
# The default is to query one nameserver and stop (false).
#query_all_nameservers = true
# Enables using *only* TCP for querying your specified nameservers instead of UDP.
#
# You very likely do *not* want this. hickory-resolver already falls back to TCP on UDP errors.
# Defaults to false
#query_over_tcp_only = false
# DNS A/AAAA record lookup strategy
#
# Takes a number of one of the following options:
# 1 - Ipv4Only (Only query for A records, no AAAA/IPv6)
# 2 - Ipv6Only (Only query for AAAA records, no A/IPv4)
# 3 - Ipv4AndIpv6 (Query for A and AAAA records in parallel, uses whatever returns a successful response first)
# 4 - Ipv6thenIpv4 (Query for AAAA record, if that fails then query the A record)
# 5 - Ipv4thenIpv6 (Query for A record, if that fails then query the AAAA record)
#
# If you don't have IPv6 networking, then for better performance it may be suitable to set this to Ipv4Only (1) as
# you will never ever use the AAAA record contents even if the AAAA record is successful instead of the A record.
#
# Defaults to 5 - Ipv4ThenIpv6 as this is the most compatible and IPv4 networking is currently the most prevalent.
#ip_lookup_strategy = 5
### Request Timeouts, Connection Timeouts, and Connection Pooling
@@ -585,8 +640,8 @@ allow_profile_lookup_federation_requests = true
# Appservice URL request connection timeout
#
# Defaults to 120 seconds
#appservice_timeout = 120
# Defaults to 35 seconds as generally appservices are hosted within the same network
#appservice_timeout = 35
# Appservice URL idle connection pool timeout
#
+14 -26
View File
@@ -1,34 +1,22 @@
conduwuit for Debian
==================
# conduwuit for Debian
Installation
------------
Information about downloading and deploying the Debian package. This may also be referenced for other `apt`-based distros such as Ubuntu.
Information about downloading, building and deploying the Debian package, see
the "Installing conduwuit" section in the Deploying docs.
All following sections until "Setting up the Reverse Proxy" be ignored because
this is handled automatically by the packaging.
### Installation
Configuration
-------------
It is recommended to see the [generic deployment guide](../deploying/generic.md) for further information if needed as usage of the Debian package is generally related.
When installed, Debconf generates the configuration of the homeserver
(host)name, the address and port it listens on. This configuration ends up in
`/etc/conduwuit/conduwuit.toml`.
### Configuration
You can tweak more detailed settings by uncommenting and setting the variables
in `/etc/conduwuit/conduwuit.toml`. This involves settings such as the maximum
file size for download/upload, enabling federation, etc.
When installed, the example config is placed at `/etc/conduwuit/conduwuit.toml` as the default config. At the minimum, you will need to change your `server_name` here.
Running
-------
You can tweak more detailed settings by uncommenting and setting the config options
in `/etc/conduwuit/conduwuit.toml`.
The package uses the `conduwuit.service` systemd unit file to start and
stop conduwuit. It loads the configuration file mentioned above to set up the
environment before running the server.
### Running
This package assumes by default that conduwuit will be placed behind a reverse
proxy. This default deployment entails just listening
on `127.0.0.1` and the free port `6167` and is reachable via a client using the URL
<http://localhost:6167>. Matrix federation requires TLS, so you will need to set up
some certificates and renewal, for it to work properly.
The package uses the [`conduwuit.service`](../configuration.md#example-systemd-unit-file) systemd unit file to start and stop conduwuit. The binary is installed at `/usr/sbin/conduwuit`.
This package assumes by default that conduwuit will be placed behind a reverse proxy. The default config options apply (listening on `localhost` and TCP port `6167`). Matrix federation requires a valid domain name and TLS, so you will need to set up TLS certificates and renewal for it to work properly if you intend to federate.
Consult various online documentation and guides on setting up a reverse proxy and TLS. Caddy is documented at the [generic deployment guide](../deploying/generic.md#setting-up-the-reverse-proxy) as it's the easiest and most user friendly.
+13 -9
View File
@@ -1,13 +1,20 @@
[Unit]
Description=conduwuit Matrix homeserver
Documentation=https://conduwuit.puppyirl.gay/
After=network-online.target
[Service]
DynamicUser=yes
User=_conduwuit
Group=_conduwuit
User=conduwuit
Group=conduwuit
Type=notify
Environment="CONDUWUIT_CONFIG=/etc/conduwuit/conduwuit.toml"
ExecStart=/usr/sbin/conduwuit
ReadWritePaths=/var/lib/conduwuit /etc/conduwuit
AmbientCapabilities=
CapabilityBoundingSet=
@@ -39,19 +46,16 @@ SystemCallArchitectures=native
SystemCallFilter=@system-service @resources
SystemCallFilter=~@clock @debug @module @mount @reboot @swap @cpu-emulation @obsolete @timer @chown @setuid @privileged @keyring @ipc
SystemCallErrorNumber=EPERM
StateDirectory=matrix-conduit
#StateDirectory=conduwuit
RuntimeDirectory=conduit
RuntimeDirectory=conduwuit
RuntimeDirectoryMode=0750
Environment="CONDUIT_CONFIG=/etc/conduwuit/conduwuit.toml"
ExecStart=/usr/sbin/conduwuit
Restart=on-failure
RestartSec=5
TimeoutStopSec=4m
TimeoutStartSec=4m
TimeoutStopSec=2m
TimeoutStartSec=2m
StartLimitInterval=1m
StartLimitBurst=5
+12 -11
View File
@@ -1,17 +1,18 @@
#!/bin/sh
set -e
# TODO: implement debconf support that is maintainable without duplicating the config
# Source debconf library.
. /usr/share/debconf/confmodule
# Ask for the Matrix homeserver name, address and port.
db_input high conduwuit/hostname || true
db_go
db_input low conduwuit/address || true
db_go
db_input medium conduwuit/port || true
db_go
#. /usr/share/debconf/confmodule
#
## Ask for the Matrix homeserver name, address and port.
#db_input high conduwuit/hostname || true
#db_go
#
#db_input low conduwuit/address || true
#db_go
#
#db_input medium conduwuit/port || true
#db_go
exit 0
+25 -10
View File
@@ -1,28 +1,43 @@
#!/bin/sh
set -e
. /usr/share/debconf/confmodule
# TODO: implement debconf support that is maintainable without duplicating the config
#. /usr/share/debconf/confmodule
CONDUWUIT_DATABASE_PATH=/var/lib/conduwuit/
CONDUWUIT_DATABASE_PATH=/var/lib/conduwuit
CONDUWUIT_CONFIG_PATH=/etc/conduwuit
CONDUWUIT_CONFIG_FILE="${CONDUWUIT_CONFIG_PATH}/conduwuit.toml"
case "$1" in
configure)
# Create the `_conduwuit` user if it does not exist yet.
if ! getent passwd _conduwuit > /dev/null ; then
# Create the `conduwuit` user if it does not exist yet.
if ! getent passwd conduwuit > /dev/null ; then
echo 'Adding system user for the conduwuit Matrix homeserver' 1>&2
adduser --system --group --quiet \
--home "$CONDUWUIT_DATABASE_PATH" \
--disabled-login \
--shell "/usr/sbin/nologin" \
--force-badname \
_conduwuit
--verbose \
conduwuit
fi
# Create the database path if it does not exist yet and fix up ownership
# and permissions.
mkdir -p "$CONDUWUIT_DATABASE_PATH"
chown _conduwuit:_conduwuit -R "$CONDUWUIT_DATABASE_PATH"
chmod 700 "$CONDUWUIT_DATABASE_PATH"
# and permissions for the config.
mkdir -v -p "$CONDUWUIT_DATABASE_PATH"
# symlink the previous location for compatibility
ln -s -v "$CONDUWUIT_DATABASE_PATH" "/var/lib/matrix-conduit"
chown -v conduwuit:conduwuit -R "$CONDUWUIT_DATABASE_PATH"
chown -v conduwuit:conduwuit -R "$CONDUWUIT_CONFIG_PATH"
chmod -v 740 "$CONDUWUIT_DATABASE_PATH"
echo ''
echo 'Make sure you edit the example config at /etc/conduwuit/conduwuit.toml before starting!'
echo 'To start the server, run: systemctl start conduwuit.service'
echo ''
;;
esac
+8 -3
View File
@@ -1,10 +1,11 @@
#!/bin/sh
set -e
. /usr/share/debconf/confmodule
#. /usr/share/debconf/confmodule
CONDUWUIT_CONFIG_PATH=/etc/conduwuit
CONDUWUIT_DATABASE_PATH=/var/lib/conduwuit
CONDUWUIT_DATABASE_PATH_SYMLINK=/var/lib/matrix-conduit
case $1 in
purge)
@@ -15,11 +16,15 @@ case $1 in
# "configuration files must be preserved when the package is removed, and
# only deleted when the package is purged."
if [ -d "$CONDUWUIT_CONFIG_PATH" ]; then
rm -r "$CONDUWUIT_CONFIG_PATH"
rm -v -r "$CONDUWUIT_CONFIG_PATH"
fi
if [ -d "$CONDUWUIT_DATABASE_PATH" ]; then
rm -r "$CONDUWUIT_DATABASE_PATH"
rm -v -r "$CONDUWUIT_DATABASE_PATH"
fi
if [ -d "$CONDUWUIT_DATABASE_PATH_SYMLINK" ]; then
rm -v -r "$CONDUWUIT_DATABASE_PATH_SYMLINK"
fi
;;
esac
-21
View File
@@ -1,21 +0,0 @@
Template: conduwuit/hostname
Type: string
Default: localhost
Description: The server (host)name of the Matrix homeserver
This is the hostname the homeserver will be reachable at via a client.
.
If set to "localhost", you can connect with a client locally and clients
from other hosts and also other homeservers will not be able to reach you!
Template: conduwuit/address
Type: string
Default: 127.0.0.1
Description: The listen address of the Matrix homeserver
This is the address the homeserver will listen on. Leave it set to 127.0.0.1
when using a reverse proxy.
Template: conduwuit/port
Type: string
Default: 6167
Description: The port of the Matrix homeserver
This port is most often just accessed by a reverse proxy.
+35
View File
@@ -0,0 +1,35 @@
[package]
name = "rust-rocksdb-uwu"
version = "0.0.1"
edition = "2021"
[features]
default = ["snappy", "lz4", "zstd", "zlib", "bzip2"]
jemalloc = ["rust-rocksdb/jemalloc"]
io-uring = ["rust-rocksdb/io-uring"]
valgrind = ["rust-rocksdb/valgrind"]
snappy = ["rust-rocksdb/snappy"]
lz4 = ["rust-rocksdb/lz4"]
zstd = ["rust-rocksdb/zstd"]
zlib = ["rust-rocksdb/zlib"]
bzip2 = ["rust-rocksdb/bzip2"]
rtti = ["rust-rocksdb/rtti"]
mt_static = ["rust-rocksdb/mt_static"]
multi-threaded-cf = ["rust-rocksdb/multi-threaded-cf"]
serde1 = ["rust-rocksdb/serde1"]
malloc-usable-size = ["rust-rocksdb/malloc-usable-size"]
[dependencies.rust-rocksdb]
git = "https://github.com/zaidoon1/rust-rocksdb"
branch = "master"
default-features = false
[lib]
path = "lib.rs"
crate-type = [
"rlib",
# "dylib"
]
[lints]
workspace = true
+61
View File
@@ -0,0 +1,61 @@
pub use rust_rocksdb::*;
#[cfg_attr(not(conduit_mods), link(name = "rocksdb"))]
#[cfg_attr(conduit_mods, link(name = "rocksdb", kind = "static"))]
extern "C" {
pub fn rocksdb_list_column_families();
pub fn rocksdb_logger_create_stderr_logger();
pub fn rocksdb_options_set_info_log();
pub fn rocksdb_get_options_from_string();
pub fn rocksdb_writebatch_create();
pub fn rocksdb_writebatch_destroy();
pub fn rocksdb_writebatch_put_cf();
pub fn rocksdb_writebatch_delete_cf();
pub fn rocksdb_iter_value();
pub fn rocksdb_iter_seek_to_last();
pub fn rocksdb_iter_seek_for_prev();
pub fn rocksdb_iter_seek_to_first();
pub fn rocksdb_iter_next();
pub fn rocksdb_iter_prev();
pub fn rocksdb_iter_seek();
pub fn rocksdb_iter_valid();
pub fn rocksdb_iter_get_error();
pub fn rocksdb_iter_key();
pub fn rocksdb_iter_destroy();
pub fn rocksdb_livefiles();
pub fn rocksdb_livefiles_count();
pub fn rocksdb_livefiles_destroy();
pub fn rocksdb_livefiles_column_family_name();
pub fn rocksdb_livefiles_name();
pub fn rocksdb_livefiles_size();
pub fn rocksdb_livefiles_level();
pub fn rocksdb_livefiles_smallestkey();
pub fn rocksdb_livefiles_largestkey();
pub fn rocksdb_livefiles_entries();
pub fn rocksdb_livefiles_deletions();
pub fn rocksdb_put_cf();
pub fn rocksdb_delete_cf();
pub fn rocksdb_get_pinned_cf();
pub fn rocksdb_create_column_family();
pub fn rocksdb_get_latest_sequence_number();
pub fn rocksdb_batched_multi_get_cf();
pub fn rocksdb_cancel_all_background_work();
pub fn rocksdb_repair_db();
pub fn rocksdb_list_column_families_destroy();
pub fn rocksdb_flush();
pub fn rocksdb_flush_wal();
pub fn rocksdb_open_column_families();
pub fn rocksdb_open_for_read_only_column_families();
pub fn rocksdb_open_as_secondary_column_families();
pub fn rocksdb_open_column_families_with_ttl();
pub fn rocksdb_open();
pub fn rocksdb_open_for_read_only();
pub fn rocksdb_open_with_ttl();
pub fn rocksdb_open_as_secondary();
pub fn rocksdb_write();
pub fn rocksdb_create_iterator_cf();
pub fn rocksdb_backup_engine_create_new_backup_flush();
pub fn rocksdb_backup_engine_options_create();
pub fn rocksdb_write_buffer_manager_destroy();
pub fn rocksdb_options_set_ttl();
}
-19
View File
@@ -1,19 +0,0 @@
#!/bin/sh
# If the config file does not contain a default port and the CONDUIT_PORT env is not set, create
# try to get port from process list
if [ -z "${CONDUIT_PORT}" ]; then
CONDUIT_PORT=$(ss -tlpn | grep conduit | grep -m1 -o ':[0-9]*' | grep -m1 -o '[0-9]*')
fi
# If CONDUIT_ADDRESS is not set try to get the address from the process list
if [ -z "${CONDUIT_ADDRESS}" ]; then
CONDUIT_ADDRESS=$(ss -tlpn | awk -F ' +|:' '/conduit/ { print $4 }')
fi
# The actual health check.
# We try to first get a response on HTTP and when that fails on HTTPS and when that fails, we exit with code 1.
# TODO: Change this to a single wget call. Do we have a config value that we can check for that?
wget --no-verbose --tries=1 --spider "http://${CONDUIT_ADDRESS}:${CONDUIT_PORT}/_matrix/client/versions" || \
wget --no-verbose --tries=1 --spider "https://${CONDUIT_ADDRESS}:${CONDUIT_PORT}/_matrix/client/versions" || \
exit 1
+7 -3
View File
@@ -2,14 +2,18 @@
- [Introduction](introduction.md)
- [Differences from upstream Conduit](differences.md)
- [Example configuration](configuration.md)
- [Deploying](deploying.md)
- [Generic](deploying/generic.md)
- [Debian](deploying/debian.md)
- [Docker](deploying/docker.md)
- [NixOS](deploying/nixos.md)
- [Docker](deploying/docker.md)
- [Arch Linux](deploying/arch-linux.md)
- [Debian](deploying/debian.md)
- [TURN](turn.md)
- [Appservices](appservices.md)
- [Maintenance](maintenance.md)
- [Troubleshooting](troubleshooting.md)
- [Development](development.md)
- [Contributing](contributing.md)
- [Testing](development/testing.md)
- [Hot Reloading ("Live" Development)](development/hot_reload.md)
+6
View File
@@ -3,3 +3,9 @@
``` toml
{{#include ../conduwuit-example.toml}}
```
# Example systemd unit file
```
{{#include ../debian/conduwuit.service}}
```
+1
View File
@@ -0,0 +1 @@
{{#include ../CONTRIBUTING.md}}
+8
View File
@@ -0,0 +1,8 @@
# conduwuit for Arch Linux
Currently conduwuit is only on the Arch User Repository (AUR).
The conduwuit AUR packages are community maintained and are not maintained by conduwuit development team, but the AUR package maintainers are in the Matrix room. Please attempt to verify your AUR package's PKGBUILD file looks fine before asking for support.
- [conduwuit](https://aur.archlinux.org/packages/conduwuit) - latest tagged conduwuit
- [conduwuit-git](https://aur.archlinux.org/packages/conduwuit-git) - latest git conduwuit from `main` branch
+17 -27
View File
@@ -1,40 +1,30 @@
# Conduit - Behind Traefik Reverse Proxy
# conduwuit - Behind Traefik Reverse Proxy
version: '2.4' # uses '2.4' for cpuset
services:
homeserver:
### If you already built the Conduit image with 'docker build' or want to use the Docker Hub image,
### If you already built the conduduwit image with 'docker build' or want to use the Docker Hub image,
### then you are ready to go.
image: girlbossceo/conduwuit:latest
### If you want to build a fresh image from the sources, then comment the image line and uncomment the
### build lines. If you want meaningful labels in your built Conduit image, you should run docker compose like this:
### CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker compose up -d
# build:
# context: .
# args:
# CREATED: '2021-03-16T08:18:27Z'
# VERSION: '0.1.0'
# LOCAL: 'false'
# GIT_REF: origin/master
restart: unless-stopped
volumes:
- db:/var/lib/matrix-conduit
#- ./conduwuit.toml:/etc/conduit.toml
- db:/var/lib/conduwuit
#- ./conduwuit.toml:/etc/conduwuit.toml
networks:
- proxy
environment:
CONDUIT_SERVER_NAME: your.server.name # EDIT THIS
CONDUIT_DATABASE_PATH: /var/lib/matrix-conduit
CONDUIT_DATABASE_BACKEND: rocksdb
CONDUIT_PORT: 6167
CONDUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
CONDUIT_ALLOW_REGISTRATION: 'true'
CONDUIT_ALLOW_FEDERATION: 'true'
CONDUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
CONDUIT_TRUSTED_SERVERS: '["matrix.org"]'
#CONDUIT_LOG: warn,state_res=warn
CONDUIT_ADDRESS: 0.0.0.0
#CONDUIT_CONFIG: './conduwuit.toml' # Uncomment if you mapped config toml above
CONDUWUIT_SERVER_NAME: your.server.name # EDIT THIS
CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit
CONDUWUIT_DATABASE_BACKEND: rocksdb
CONDUWUIT_PORT: 6167
CONDUWUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
CONDUWUIT_ALLOW_REGISTRATION: 'true'
CONDUWUIT_ALLOW_FEDERATION: 'true'
CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
#CONDUWUIT_LOG: warn,state_res=warn
CONDUWUIT_ADDRESS: 0.0.0.0
#CONDUWUIT_CONFIG: './conduwuit.toml' # Uncomment if you mapped config toml above
#cpuset: "0-4" # Uncomment to limit to specific CPU cores
# We need some way to server the client and server .well-known json. The simplest way is to use a nginx container
@@ -48,7 +38,7 @@ services:
- ./nginx/www:/var/www/ # location of the client and server .well-known-files
### Uncomment if you want to use your own Element-Web App.
### Note: You need to provide a config.json for Element and you also need a second
### Domain or Subdomain for the communication between Element and Conduit
### Domain or Subdomain for the communication between Element and conduwuit
### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md
# element-web:
# image: vectorim/element-web:latest
+5 -5
View File
@@ -1,4 +1,4 @@
# Conduit - Traefik Reverse Proxy Labels
# conduwuit - Traefik Reverse Proxy Labels
version: '2.4' # uses '2.4' for cpuset
services:
@@ -7,10 +7,10 @@ services:
- "traefik.enable=true"
- "traefik.docker.network=proxy" # Change this to the name of your Traefik docker proxy network
- "traefik.http.routers.to-conduit.rule=Host(`<SUBDOMAIN>.<DOMAIN>`)" # Change to the address on which Conduit is hosted
- "traefik.http.routers.to-conduit.tls=true"
- "traefik.http.routers.to-conduit.tls.certresolver=letsencrypt"
- "traefik.http.routers.to-conduit.middlewares=cors-headers@docker"
- "traefik.http.routers.to-conduwuit.rule=Host(`<SUBDOMAIN>.<DOMAIN>`)" # Change to the address on which conduwuit is hosted
- "traefik.http.routers.to-conduwuit.tls=true"
- "traefik.http.routers.to-conduwuit.tls.certresolver=letsencrypt"
- "traefik.http.routers.to-conduwuit.middlewares=cors-headers@docker"
- "traefik.http.middlewares.cors-headers.headers.accessControlAllowOriginList=*"
- "traefik.http.middlewares.cors-headers.headers.accessControlAllowHeaders=Origin, X-Requested-With, Content-Type, Accept, Authorization"
+19 -30
View File
@@ -1,44 +1,33 @@
# Conduit - Behind Traefik Reverse Proxy
# conduwuit - Behind Traefik Reverse Proxy
version: '2.4' # uses '2.4' for cpuset
services:
homeserver:
### If you already built the Conduit image with 'docker build' or want to use the Docker Hub image,
### If you already built the conduwuit image with 'docker build' or want to use the Docker Hub image,
### then you are ready to go.
image: girlbossceo/conduwuit:latest
### If you want to build a fresh image from the sources, then comment the image line and uncomment the
### build lines. If you want meaningful labels in your built Conduit image, you should run docker compose like this:
### CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker compose up -d
# build:
# context: .
# args:
# CREATED: '2021-03-16T08:18:27Z'
# VERSION: '0.1.0'
# LOCAL: 'false'
# GIT_REF: origin/master
restart: unless-stopped
volumes:
- db:/srv/conduit/.local/share/conduit
#- ./conduwuit.toml:/etc/conduit.toml
- db:/srv/conduwuit/.local/share/conduwuit
#- ./conduwuit.toml:/etc/conduwuit.toml
networks:
- proxy
environment:
CONDUIT_SERVER_NAME: your.server.name # EDIT THIS
CONDUIT_TRUSTED_SERVERS: '["matrix.org"]'
CONDUIT_ALLOW_REGISTRATION : 'true'
#CONDUIT_CONFIG: './conduwuit.toml' # Uncomment if you mapped config toml above
CONDUWUIT_SERVER_NAME: your.server.name # EDIT THIS
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
CONDUWUIT_ALLOW_REGISTRATION : 'true'
#CONDUWUIT_CONFIG: './conduwuit.toml' # Uncomment if you mapped config toml above
### Uncomment and change values as desired
# CONDUIT_ADDRESS: 0.0.0.0
# CONDUIT_PORT: 6167
# Available levels are: error, warn, info, debug, trace - more info at: https://docs.rs/env_logger/*/env_logger/#enabling-logging
# CONDUIT_LOG: info # default is: "warn,state_res=warn"
# CONDUIT_ALLOW_JAEGER: 'false'
# CONDUIT_ALLOW_ENCRYPTION: 'true'
# CONDUIT_ALLOW_FEDERATION: 'true'
# CONDUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
# CONDUIT_DATABASE_PATH: /srv/conduit/.local/share/conduit
# CONDUIT_WORKERS: 10
# CONDUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
# CONDUWUIT_ADDRESS: 0.0.0.0
# CONDUWUIT_PORT: 6167
# CONDUWUIT_LOG: info # default is: "warn,state_res=warn"
# CONDUWUIT_ALLOW_JAEGER: 'false'
# CONDUWUIT_ALLOW_ENCRYPTION: 'true'
# CONDUWUIT_ALLOW_FEDERATION: 'true'
# CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
# CONDUWUIT_DATABASE_PATH: /srv/conduwuit/.local/share/conduwuit
# CONDUWUIT_WORKERS: 10
# CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
#cpuset: "0-4" # Uncomment to limit to specific CPU cores
# We need some way to server the client and server .well-known json. The simplest way is to use a nginx container
@@ -53,7 +42,7 @@ services:
### Uncomment if you want to use your own Element-Web App.
### Note: You need to provide a config.json for Element and you also need a second
### Domain or Subdomain for the communication between Element and Conduit
### Domain or Subdomain for the communication between Element and conduwuit
### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md
# element-web:
# image: vectorim/element-web:latest
+17 -27
View File
@@ -1,45 +1,35 @@
# Conduit
# conduwuit
version: '2.4' # uses '2.4' for cpuset
services:
homeserver:
### If you already built the Conduit image with 'docker build' or want to use a registry image,
### If you already built the conduwuit image with 'docker build' or want to use a registry image,
### then you are ready to go.
image: girlbossceo/conduwuit:latest
### If you want to build a fresh image from the sources, then comment the image line and uncomment the
### build lines. If you want meaningful labels in your built Conduit image, you should run docker compose like this:
### CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker compose up -d
# build:
# context: .
# args:
# CREATED: '2021-03-16T08:18:27Z'
# VERSION: '0.1.0'
# LOCAL: 'false'
# GIT_REF: origin/master
restart: unless-stopped
ports:
- 8448:6167
volumes:
- db:/var/lib/matrix-conduit
#- ./conduwuit.toml:/etc/conduit.toml
- db:/var/lib/conduwuit
#- ./conduwuit.toml:/etc/conduwuit.toml
environment:
CONDUIT_SERVER_NAME: your.server.name # EDIT THIS
CONDUIT_DATABASE_PATH: /var/lib/matrix-conduit
CONDUIT_DATABASE_BACKEND: rocksdb
CONDUIT_PORT: 6167
CONDUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
CONDUIT_ALLOW_REGISTRATION: 'true'
CONDUIT_ALLOW_FEDERATION: 'true'
CONDUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
CONDUIT_TRUSTED_SERVERS: '["matrix.org"]'
#CONDUIT_LOG: warn,state_res=warn
CONDUIT_ADDRESS: 0.0.0.0
#CONDUIT_CONFIG: './conduwuit.toml' # Uncomment if you mapped config toml above
CONDUWUIT_SERVER_NAME: your.server.name # EDIT THIS
CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit
CONDUWUIT_DATABASE_BACKEND: rocksdb
CONDUWUIT_PORT: 6167
CONDUWUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
CONDUWUIT_ALLOW_REGISTRATION: 'true'
CONDUWUIT_ALLOW_FEDERATION: 'true'
CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
#CONDUWUIT_LOG: warn,state_res=warn
CONDUWUIT_ADDRESS: 0.0.0.0
#CONDUWUIT_CONFIG: './conduwuit.toml' # Uncomment if you mapped config toml above
#cpuset: "0-4" # Uncomment to limit to specific CPU cores
#
### Uncomment if you want to use your own Element-Web App.
### Note: You need to provide a config.json for Element and you also need a second
### Domain or Subdomain for the communication between Element and Conduit
### Domain or Subdomain for the communication between Element and conduwuit
### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md
# element-web:
# image: vectorim/element-web:latest
+9 -113
View File
@@ -12,18 +12,17 @@ OCI images for conduwuit are available in the registries listed below.
| Registry | Image | Size | Notes |
| --------------- | --------------------------------------------------------------- | ----------------------------- | ---------------------- |
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:latest][gh] | ![Image Size][shield-latest] | Stable tagged image. |
| GitLab Registry | [registry.gitlab.com/conduwuit/conduwuit:latest][gl] | ![Image Size][shield-latest] | Stable tagged image. |
| Docker Hub | [docker.io/girlbossceo/conduwuit:latest][dh] | ![Image Size][shield-latest] | Stable tagged image. |
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:main][gh] | ![Image Size][shield-main] | Stable branch. |
| Docker Hub | [docker.io/girlbossceo/conduwuit:main][dh] | ![Image Size][shield-main] | Stable branch. |
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:dev][gh] | ![Image Size][shield-main] | Development version. |
| Docker Hub | [docker.io/girlbossceo/conduwuit:dev][dh] | ![Image Size][shield-dev] | Development version. |
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:main][gh] | ![Image Size][shield-main] | Stable main branch. |
| GitLab Registry | [registry.gitlab.com/conduwuit/conduwuit:main][gl] | ![Image Size][shield-main] | Stable main branch. |
| Docker Hub | [docker.io/girlbossceo/conduwuit:main][dh] | ![Image Size][shield-main] | Stable main branch. |
[dh]: https://hub.docker.com/repository/docker/girlbossceo/conduwuit
[gh]: https://github.com/girlbossceo/conduwuit/pkgs/container/conduwuit
[gl]: https://gitlab.com/conduwuit/conduwuit/container_registry/6351657
[shield-latest]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/latest
[shield-main]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/main
[shield-dev]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/dev
Use
@@ -33,24 +32,6 @@ docker image pull <link>
to pull it to your machine.
### Build using a Dockerfile
The Dockerfile provided by conduwuit has two stages, each of which creates an image.
1. **Builder:** Builds the binary from local context or by cloning a git revision from the official repository.
2. **Runner:** Copies the built binary from **Builder** and sets up the runtime environment, like creating a volume to persist the database and applying the correct permissions.
To build the image you can use the following command
```bash
docker build --tag girlbossceo/conduwuit:main .
```
which also will tag the resulting image as `girlbossceo/conduwuit:main`.
### Run
When you have the image you can simply run it with
@@ -70,9 +51,9 @@ docker run -d -p 8448:6167 \
or you can use [docker compose](#docker-compose).
The `-d` flag lets the container run in detached mode. You now need to supply a `conduwuit.toml` config file, an example can be found [here](../configuration.md).
You can pass in different env vars to change config values on the fly. You can even configure conduwuit completely by using env vars, but for that you need
to pass `-e CONDUIT_CONFIG=""` into your container. For an overview of possible values, please take a look at the `docker-compose.yml` file.
The `-d` flag lets the container run in detached mode. You may supply an optional `conduwuit.toml` config file, the example config can be found [here](../configuration.md).
You can pass in different env vars to change config values on the fly. You can even configure conduwuit completely by using env vars. For an overview of possible
values, please take a look at the [`docker-compose.yml`](docker-compose.yml) file.
If you just want to test conduwuit for a short time, you can use the `--rm` flag, which will clean up everything related to your container after you stop it.
@@ -126,92 +107,7 @@ either expose ports `443` and `8448` or serve two endpoints `.well-known/matrix/
With the service `well-known` we use a single `nginx` container that will serve those two files.
So...step by step:
1. Copy [`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml) (or
[`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml)) and [`docker-compose.override.yml`](docker-compose.override.yml) from the repository and remove `.for-traefik` (or `.with-traefik`) from the filename.
2. Open both files and modify/adjust them to your needs. Meaning, change the `CONDUIT_SERVER_NAME` and the volume host mappings according to your needs.
3. Create the `conduwuit.toml` config file, an example can be found [here](../configuration.md), or set `CONDUIT_CONFIG=""` and configure conduwuit per env vars.
4. Uncomment the `element-web` service if you want to host your own Element Web Client and create a `element_config.json`.
5. Create the files needed by the `well-known` service.
- `./nginx/matrix.conf` (relative to the compose file, you can change this, but then also need to change the volume mapping)
```nginx
server {
server_name <SUBDOMAIN>.<DOMAIN>;
listen 80 default_server;
location /.well-known/matrix/server {
return 200 '{"m.server": "<SUBDOMAIN>.<DOMAIN>:443"}';
types { } default_type "application/json; charset=utf-8";
}
location /.well-known/matrix/client {
return 200 '{"m.homeserver": {"base_url": "https://<SUBDOMAIN>.<DOMAIN>"}}';
types { } default_type "application/json; charset=utf-8";
add_header "Access-Control-Allow-Origin" *;
}
location / {
return 404;
}
}
```
6. Run `docker compose up -d`
7. Connect to your homeserver with your preferred client and create a user. You should do this immediately after starting Conduit, because the first created user is the admin.
## Voice communication
In order to make or receive calls, a TURN server is required. conduwuit suggests using [Coturn](https://github.com/coturn/coturn) for this purpose, which is also available as a Docker image. Before proceeding with the software installation, it is essential to have the necessary configurations in place.
### Configuration
Create a configuration file called `coturn.conf` containing:
```conf
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 conduwuit. You can either modify conduwuit.toml to include these lines:
```
turn_uris = ["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]
turn_secret = "<secret key from coturn configuration>"
```
or append the following to the docker environment variables dependig on which configuration method you used earlier:
```yml
CONDUIT_TURN_URIS: '["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]'
CONDUIT_TURN_SECRET: "<secret key from coturn configuration>"
```
Restart Conduit to apply these changes.
### 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 the following link: https://github.com/coturn/coturn/blob/master/docker/coturn/README.md.
For security recommendations see Synapse's [Coturn documentation](https://github.com/matrix-org/synapse/blob/develop/docs/setup/turn/coturn.md#configuration).
See the [TURN](../turn.md) page.
+20 -50
View File
@@ -1,7 +1,5 @@
# Generic deployment documentation
### Please note that this documentation is not fully representative of conduwuit at the moment. Assume majority of it is outdated.
> ## Getting help
>
> If you run into any problems while setting up conduwuit, ask us
@@ -11,25 +9,18 @@
You may simply download the binary that fits your machine. Run `uname -m` to see what you need.
Prebuilt binaries can be downloaded from the latest successful CI workflow on the main branch here: https://github.com/girlbossceo/conduwuit/actions/workflows/ci.yml?query=branch%3Amain+actor%3Agirlbossceo+is%3Asuccess+event%3Apush
Prebuilt binaries can be downloaded from the latest tagged release [here](https://github.com/girlbossceo/conduwuit/releases/latest).
Alternatively, you may compile the binary yourself. First, install any dependencies:
The latest tagged release also includes the Debian packages.
```bash
# Debian
$ sudo apt install libclang-dev build-essential
Alternatively, you may compile the binary yourself. We recommend using [Lix](https://lix.systems) to build conduwuit as this has the most guaranteed
reproducibiltiy and easiest to get a build environment and output going.
# RHEL
$ sudo dnf install clang
```
Then, `cd` into the source tree of conduwuit and run:
```bash
$ cargo build --release
```
Otherwise, follow standard Rust project build guides (installing git and cloning the repo, getting the Rust toolchain via rustup, installing LLVM toolchain + libclang, installing liburing for io_uring and RocksDB, etc).
## Adding a conduwuit user
While conduwuit can run as any user it is usually better to use dedicated users for different services. This also allows
While conduwuit can run as any user it is better to use dedicated users for different services. This also allows
you to make sure that the file permissions are correctly set up.
In Debian or RHEL, you can use this command to create a conduwuit user:
@@ -38,6 +29,12 @@ In Debian or RHEL, you can use this command to create a conduwuit user:
sudo adduser --system conduwuit --group --disabled-login --no-create-home
```
For distros without `adduser`:
```bash
sudo useradd -r --shell /usr/bin/nologin --no-create-home conduwuit
```
## Forwarding ports in the firewall or the router
conduwuit uses the ports 443 and 8448 both of which need to be open in the firewall.
@@ -46,45 +43,18 @@ If conduwuit runs behind a router or in a container and has a different public I
## Setting up a systemd service
Now we'll set up a systemd service for conduwuit, so it's easy to start/stop conduwuit and set it to autostart when your
server reboots. Simply paste the default systemd service you can find below into
`/etc/systemd/system/conduwuit.service`.
```systemd
[Unit]
Description=conduwuit Matrix Server
After=network.target
[Service]
Environment="CONDUWUIT_CONFIG=/etc/conduwuit/conduwuit.toml"
User=conduwuit
Group=conduwuit
RuntimeDirectory=conduwuit
RuntimeDirectoryMode=0750
Restart=always
ExecStart=/usr/local/bin/conduwuit
[Install]
WantedBy=multi-user.target
```
Finally, run
```bash
$ sudo systemctl daemon-reload
```
The systemd unit for conduwuit can be found [here](../configuration.md#example-systemd-unit-file). You may need to change the `ExecStart=` path to where you placed the conduwuit binary.
## Creating the conduwuit configuration file
Now we need to create the conduwuit's config file in `/etc/conduwuit/conduwuit.toml`. Paste this in **and take a moment
to read it. You need to change at least the server name.**
RocksDB (`rocksdb`) is the only supported database backend. SQLite only exists for historical reasons and is not recommended. Any performance issues, storage issues, database issues, etc will not be assisted if using SQLite and you will be asked to migrate to RocksDB first.
Now we need to create the conduwuit's config file in `/etc/conduwuit/conduwuit.toml`. The example config can be found at [conduwuit-example.toml](../configuration.md).**Please take a moment to read it. You need to change at least the server name.**
See the following example config at [conduwuit-example.toml](../configuration.md)
RocksDB (`rocksdb`) is the only supported database backend. SQLite only exists for historical reasons and is not recommended. Any performance issues, storage issues, database issues, etc will not be assisted if using SQLite and you will be asked to migrate to RocksDB first.
## Setting the correct file permissions
As we are using a conduwuit specific user we need to allow it to read the config. To do that you can run this command on
If you are using a dedicated user for conduwuit, you will need to allow it to read the config. To do that you can run this command on
Debian or RHEL:
```bash
@@ -102,7 +72,7 @@ sudo chmod 700 /var/lib/conduwuit/
## Setting up the Reverse Proxy
Refer to the documentation or various guides online of your chosen reverse proxy software. A Caddy example will be provided as this is the recommended reverse proxy for new users and is very trivial.
Refer to the documentation or various guides online of your chosen reverse proxy software. A [Caddy](https://caddyserver.com/) example will be provided as this is the recommended reverse proxy for new users and is very trivial to use (handles TLS, reverse proxy headers, etc transparently with proper defaults).
### Caddy
@@ -118,10 +88,10 @@ your.server.name, your.server.name:8448 {
}
```
That's it! Just start or enable the service and you're set.
That's it! Just start and enable the service and you're set.
```bash
$ sudo systemctl enable caddy
$ sudo systemctl enable --now caddy
```
## You're done!
+11 -2
View File
@@ -1,6 +1,6 @@
# conduwuit for NixOS
conduwuit can be acquired by Nix from various places:
conduwuit can be acquired by [Lix][lix] from various places:
* The `flake.nix` at the root of the repo
* The `default.nix` at the root of the repo
@@ -10,9 +10,17 @@ A binary cache for conduwuit that the CI/CD publishes to is available at the
following places (both are the same just different names):
```
https://attic.kennel.juneis.dog/conduit
conduit:Isq8FGyEC6FOXH6nD+BOeAA+bKp6X6UIbupSlGEPuOg=
conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk=
https://attic.kennel.juneis.dog/conduwuit
conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE=
```
The binary caches have been recreated recently due to attic issues. The old public keys were:
```
conduit:Isq8FGyEC6FOXH6nD+BOeAA+bKp6X6UIbupSlGEPuOg=
conduwuit:lYPVh7o1hLu1idH4Xt2QHaRa49WRGSAqzcfFd94aOTw=
```
@@ -26,5 +34,6 @@ If you want to run the latest code, you should get Conduwuit from the `flake.nix
or `default.nix` and set [`services.matrix-conduit.package`][package]
appropriately.
[lix]: https://lix.systems/
[module]: https://search.nixos.org/options?channel=unstable&query=services.matrix-conduit
[package]: https://search.nixos.org/options?channel=unstable&query=services.matrix-conduit.package
+19 -1
View File
@@ -1,4 +1,22 @@
# Development
Information about developing the project. If you are only interested in using
it, you can safely ignore this section.
it, you can safely ignore this section. If you plan on contributing, see the
[contributor's guide](contributing.md).
## Debugging with `tokio-console`
[`tokio-console`][1] can be a useful tool for debugging and profiling. To make
a `tokio-console`-enabled build of Conduwuit, enable the `tokio_console` feature,
disable the default `release_max_log_level` feature, and set the
`--cfg tokio_unstable` flag to enable experimental tokio APIs. A build might
look like this:
```bash
RUSTFLAGS="--cfg tokio_unstable" cargo build \
--release \
--no-default-features \
--features=rocksdb,systemd,element_hacks,sentry_telemetry,gzip_compression,brotli_compression,zstd_compression,tokio_console
```
[1]: https://docs.rs/tokio-console/latest/tokio_console/
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

+93
View File
@@ -0,0 +1,93 @@
# Hot Reloading ("Live" Development)
### Summary
When developing in debug-builds with the nightly toolchain, conduwuit is modular using dynamic libraries and various parts of the application are hot-reloadable while the server is running: http api handlers, admin commands, services, database, etc. These are all split up into individual workspace crates as seen in the `src/` directory. Changes to sourcecode in a crate rebuild that crate and subsequent crates depending on it. Reloading then occurs for the changed crates.
Release builds still produce static binaries which are unaffected. Rust's soundness guarantees are in full force. Thus you cannot hot-reload release binaries.
### Requirements
Currently, this development setup only works on x86_64 and aarch64 Linux glibc. [musl explicitly does not support hot reloadable libraries, and does not implement `dlclose`][2]. macOS does not fully support our usage of `RTLD_GLOBAL` possibly due to some thread-local issues. [This Rust issue][3] may be of relevance, specifically [this comment][4]. It may be possible to get it working on only very modern macOS versions such as at least Sonoma, as currently loading dylibs is supported, but not unloading them in our setup, and the cited comment mentions an Apple WWDC confirming there have been TLS changes to somewhat make this possible.
As mentioned above this requires the nightly toolchain. This is due to reliance on various Cargo.toml features that are only available on nightly, most specifically `RUSTFLAGS` in Cargo.toml. Some of the implementation could also be simpler based on other various nightly features. We hope lots of nightly features start making it out of nightly sooner as there have been dozens of very helpful features that have been stuck in nightly ("unstable") for at least 5+ years that would make this simpler. We encourage greater community consensus to move these features into stability.
This currently only works on x86_64/aarch64 Linux with a glibc C library. musl C library, macOS, and likely other host architectures are not supported (if other architectures work, feel free to let us know and/or make a PR updating this). This should work on GNU ld and lld (rust-lld) and gcc/clang, however if you happen to have linker issues it's recommended to try using `mold` or `gold` linkers, and please let us know in the [conduwuit Matrix room][7] the linker error and what linker solved this issue so we can figure out a solution. Ideally there should be minimal friction to using this, and in the future a build script (`build.rs`) may be suitable to making this easier to use if the capabilities allow us.
### Usage
As of 19 May 2024, the instructions for using this are:
0. Have patience. Don't hesitate to join the [conduwuit Matrix room][7] to receive help using this. As indicated by the various rustflags used and some of the interesting issues linked at the bottom, this is definitely not something the Rust ecosystem or toolchain is used to doing.
1. Install the nightly toolchain using rustup. You may need to use `rustup override set nightly` in your local conduwuit directory, or use `cargo +nightly` for all actions.
2. Uncomment `cargo-features` at the top level / root Cargo.toml
3. Scroll down to the `# Developer profile` section and uncomment ALL the rustflags for each dev profile and their respective packages.
4. In each workspace crate's Cargo.toml (everything under `src/*` AND `deps/rust-rocksdb/Cargo.toml`), uncomment the `dylib` crate type under `[lib]`.
5. Due to [this rpath issue][5], you must export the `LD_LIBRARY_PATH` environment variable to your nightly Rust toolchain library directory. If using rustup (hopefully), use this: `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/`
6. Start the server. You can use `cargo +nightly run` for this along with the standard.
7. Make some changes where you need to.
8. In a separate terminal window in the same directory (or using a terminal multiplexer like tmux), run the *build* Cargo command `cargo +nightly build`. Cargo should only rebuild what was changed / what's necessary, so it should not be rebuilding all the crates.
9. In your conduwuit server terminal, hit/send `CTRL+C` signal. This will tell conduwuit to find which libraries need to be reloaded, and reloads them as necessary.
10. If there were no errors, it will tell you it successfully reloaded `#` modules, and your changes should now be visible. Repeat 7 - 9 as needed.
To shutdown conduwuit in this setup, hit/send `CTRL+\`. Normal builds still shutdown with `CTRL+C` as usual.
Steps 1 - 5 are the initial first-time steps for using this. To remove the hot reload setup, revert/comment all the Cargo.toml changes.
As mentioned in the requirements section, if you happen to have some linker issues, try using the `-fuse-ld=` rustflag and specify mold or gold in all the `rustflags` definitions in the top level Cargo.toml, and please let us know in the [conduwuit Matrix room][7] the problem. mold can be installed typically through your distro, and gold is provided by the binutils package.
It's possible a helper script can be made to do all of this, or most preferably a specially made build script (build.rs). `cargo watch` support will be implemented soon which will eliminate the need to manually run `cargo build` all together.
### Addendum
Conduit was inherited as a single crate without modularity or reloading in its design. Reasonable partitioning and abstraction allowed a split into several crates, though many circular dependencies had to be corrected. The resulting crates now form a directed graph as depicted in figures below. The interfacing between these crates is still extremely broad which is not mitigable.
Initially [hot_lib_reload][6] was investigated but found appropriate for a project designed with modularity through limited interfaces, not a large and complex existing codebase. Instead a bespoke solution built directly on [libloading][8] satisfied our constraints. This required relatively minimal modifications and zero maintenance burden compared to what would be required otherwise. The technical difference lies with relocation processing: we leverage global bindings (`RTLD_GLOBAL`) in a very intentional way. Most libraries and off-the-shelf module systems (such as [hot_lib_reload][6]) restrict themselves to local bindings (`RTLD_LOCAL`). This allows them to release software to multiple platforms with much greater consistency, but at the cost of burdening applications to explicitly manage these bindings. In our case with an optional feature for developers, we shrug any such requirement to enjoy the cost/benefit on platforms where global relocations are properly cooperative.
To make use of `RTLD_GLOBAL` the application has to be oriented as a directed acyclic graph. The primary rule is simple and illustrated in the figure below: **no crate is allowed to call a function or use a variable from a crate below it.**
![conduwuit's dynamic library setup diagram - created by Jason Volk](assets/libraries.png)
When a symbol is referenced between crates they become bound: **crates cannot be unloaded until their calling crates are first unloaded.** Thus we start the reloading process from the crate which has no callers. There is a small problem though: the first crate is called by the base executable itself! This is solved by using an `RTLD_LOCAL` binding for just one link between the main executable and the first crate, freeing the executable from all modules as no global binding ever occurs between them.
![conduwuit's reload and load order diagram - created by Jason Volk](assets/reload_order.png)
Proper resource management is essential for reliable reloading to occur. This is a very basic ask in RAII-idiomatic Rust and the exposure to reloading hazards is remarkably low, generally stemming from poor patterns and practices. Unfortunately static analysis doesn't enforce reload-safety programmatically (though it could one day), for now hazards can be avoided by knowing a few basic do's and dont's:
1. Understand that code is memory. Just like one is forbidden from referencing free'd memory, one must not transfer control to free'd code. Exposure to this is primarily from two things:
- Callbacks, which this project makes very little use of.
- Async tasks, which are addressed below.
2. Tie all resources to a scope or object lifetime with greatest possible symmetry (locality). For our purposes this applies to code resources, which means async blocks and tokio tasks.
- **Never spawn a task without receiving and storing its JoinHandle**.
- **Always wait on join handles** before leaving a scope or in another cleanup function called by an owning scope.
3. Know any minor specific quirks documented in code or here:
- Don't use `tokio::spawn`, instead use our `Handle` in `core/server.rs`, which is reachable in most of the codebase via `services()` or other state. This is due to some bugs or assumptions made in tokio, as it happens in `unsafe {}` blocks, which are mitigated by circumventing some thread-local variables. Using runtime handles is good practice in any case.
The initial implementation PR is available [here][1].
### Interesting related issues/bugs
- [DT_RUNPATH produced in binary with rpath = true is wrong (cargo)][5]
- [Disabling MIR Optimization in Rust Compilation (cargo)](https://internals.rust-lang.org/t/disabling-mir-optimization-in-rust-compilation/19066/5)
- [Workspace-level metadata (cargo-deb)](https://github.com/kornelski/cargo-deb/issues/68)
[1]: https://github.com/girlbossceo/conduwuit/pull/387
[2]: https://wiki.musl-libc.org/functional-differences-from-glibc.html#Unloading-libraries
[3]: https://github.com/rust-lang/rust/issues/28794
[4]: https://github.com/rust-lang/rust/issues/28794#issuecomment-368693049
[5]: https://github.com/rust-lang/cargo/issues/12746
[6]: https://crates.io/crates/hot-lib-reloader/
[7]: https://matrix.to/#/#conduwuit:puppygock.gay
[8]: https://crates.io/crates/libloading
+7 -4
View File
@@ -5,13 +5,16 @@
Have a look at [Complement's repository][complement] for an explanation of what
it is.
To test against Complement, with Nix and direnv installed and set up, you can
either:
To test against Complement, with [Lix][lix] and direnv installed and set up, you can:
* Run `./bin/complement "$COMPLEMENT_SRC" ./path/to/logs.jsonl ./path/to/results.jsonl`
to build a Complement image, run the tests, and output the logs and results
to the specified paths
to the specified paths. This will also output the OCI image at `result`
* Run `nix build .#complement` from the root of the repository to just build a
Complement image
Complement OCI image outputted to `result` (it's a `.tar.gz` file)
* Or download the latest Complement OCI image from the CI workflow artifacts output
from the commit/revision you want to test (e.g. from main) [here][ci-workflows]
[lix]: https://lix.systems/
[ci-workflows]: https://github.com/girlbossceo/conduwuit/actions/workflows/ci.yml?query=event%3Apush+is%3Asuccess+actor%3Agirlbossceo
[complement]: https://github.com/matrix-org/complement
+23 -5
View File
@@ -6,7 +6,7 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
## Performance:
- Concurrency support for key fetching for faster remote room joins and room joins that will error less frequently
- Send `Cache-Control` response header with `immutable` and 1 year cache length for all media requests to instruct clients to cache media, and reduce server load from media requests that could be otherwise cached
- Send `Cache-Control` response header with `immutable` and 1 year cache length for all media requests (download and thumbnail) to instruct clients to cache media, and reduce server load from media requests that could be otherwise cached
- Add feature flags and config options to enable/build with zstd, brotli, and/or gzip HTTP body compression (response and request)
- Eliminate all usage of the thread-blocking `getaddrinfo(3)` call upon DNS queries, significantly improving federation latency/ping and cache DNS results (NXDOMAINs, successful queries, etc) using hickory-dns / hickory-resolver
- Vastly improve RocksDB default settings to use new features that help with performance significantly, uses settings tailored to SSDs, various ways to tweak RocksDB, and a conduwuit setting to tell RocksDB to use settings that are tailored to HDDs or slow spinning rust storage or buggy filesystems.
@@ -17,9 +17,12 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
- Various config options to tweak connection pooling, request timeouts, connection timeouts, DNS timeouts and settings, etc with good defaults which also help huge with performance via reusing connections and retrying where needed
- Implement building conduwuit with jemalloc (which extends to the RocksDB jemalloc feature for maximum gains) or hardened_malloc light variant, and produce CI builds with jemalloc for performance (Nix doesn't seem to build [hardened_malloc-rs](https://github.com/girlbossceo/hardened_malloc-rs) properly)
- Add support for caching DNS results with hickory-dns / hickory-resolver in conduwuit (not a replacement for a proper resolver cache, but still far better than nothing)
- Add config option for using DNS over TCP, and config option for controlling A/AAAA record lookup strategy (e.g. don't query AAAA records if you don't have IPv6 connectivity)
- Overall significant database, Client-Server, and federation performance and latency improvements (check out the ping room leaderboards if you don't believe me :>)
- Add config options for RocksDB compression and bottommost compression, including choosing the algorithm and compression level
- Use [loole](https://github.com/mahdi-shojaee/loole) MPSC channels instead of tokio MPSC channels for huge performance boosts in sending channels (mainly relevant for federation) and presence channels
- Use `tracing`/`log`'s `release_max_level_info` feature to improve performance, build speeds, binary size, and CPU usage in release builds by avoid compiling debug/trace log level macros that users will generally never use (can be disabled with a build-time feature flag)
- Enable RocksDB async read I/O via `io_uring` by default
## General Fixes:
@@ -33,12 +36,12 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
- Increased graceful shutdown timeout from a low 60 seconds to 180 seconds to avoid killing connections and let the remaining ones finish processing
- Return joined member count of rooms for push rules/conditions instead of a hardcoded value of 10
- Make `CONDUIT_CONFIG` optional, relevant for container users that configure only by environment variables and no longer need to set `CONDUIT_CONFIG` to an empty string.
- Allow HEAD HTTP requests in CORS for clients (despite not being explicity mentioned in Matrix spec, HTTP spec says all HEAD requests need to behave the same as GET requests, Synapse supports HEAD requests)
- Add missing `destination` key to all `X-Matrix` `Authorization` requests (spec compliance issue)
- Allow HEAD and PATCH (MSC4138) HTTP requests in CORS for clients (despite not being explicity mentioned in Matrix spec, HTTP spec says all HEAD requests need to behave the same as GET requests, Synapse supports HEAD requests)
- Resolve and remove some "features" from upstream that result in concurrency hazards, exponential backoff issues, or arbitrary performance limiters
- Find more servers for outbound federation `/hierarchy` requests instead of just the room ID server name
- Support for suggesting servers to join through at `/_matrix/client/v3/directory/room/{roomAlias}`
- Support for suggesting servers to join through us at `/_matrix/federation/v1/query/directory`
- Add workaround for [Out Of Your Element](https://gitdab.com/cadence/out-of-your-element) appservice bridge to make it functional on conduwuit (bug has already been reported)
## Moderation:
@@ -53,6 +56,7 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
- On new public room creations, only allow moderators to send `m.call.invite`, `org.matrix.msc3401.call`, and `org.matrix.msc3401.call.member` events
- Add support for a "global ACLs" feature (`forbidden_remote_server_names`) that blocks inbound remote room invites, room joins by room ID on server name, room joins by room alias on server name, incoming federated joins, and incoming federated room directory requests. This is very helpful for blocking servers that are purely toxic/bad and serve no value in allowing our users to suffer from things like room invite spam or such. Please note that this is not a substitute for room ACLs.
- Add support for a config option to forbid our local users from sending federated room directory requests for (`forbidden_remote_room_directory_server_names`). Similar to above, useful for blocking servers that help prevent our users from wandering into bad areas of Matrix via room directories of those malicious servers.
- Add config option for auto remediating/deactivating local non-admin users who attempt to join bad/forbidden rooms (`auto_deactivate_banned_room_attempts`)
## Privacy/Security:
@@ -66,6 +70,9 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
- Config option to disable incoming and/or outgoing remote read receipts
- Config option to disable incoming and/or outgoing remote typing indicators
- Config option to disable incoming, outgoing, and/or local presence
- Sanitise file names for the `Content-Disposition` header for all media requests (thumbnails, downloads, uploads)
- Return `inline` or `attachment` based on the detected file MIME type for the `Content-Disposition` and only allow images/videos/text/audio to be `inline`
- Send secure default HTTP headers such as a strong restrictive CSP, deny iframes, disable `X-XSS-Protection`, disable interest cohort in `Permission-Policy`, etc to mitigate any potential attack surface such as from untrusted media
## Administration/Logging:
@@ -79,7 +86,7 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
- Warn on unknown config options specified
- Add `/_conduwuit/server_version` route to return the version of conduwuit without relying on the federation API `/_matrix/federation/v1/version`
- Add configurable RocksDB recovery modes to aid in recovering corrupted RocksDB databases
- Support config options via `CONDUWUIT_` prefix
- Support config options via `CONDUWUIT_` prefix and accessing non-global struct config options with the `__` split (e.g. `CONDUWUIT_WELL_KNOWN__SERVER`)
- Add support for listening on multiple TCP ports
- Disable update check by default as it's not useful for conduwuit
- **Opt-in** Sentry.io telemetry and metrics, mainly used for crash reporting
@@ -87,7 +94,8 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
## Maintenance/Stability:
- GitLab CI ported to GitHub Actions
- Repo is mirrored to GitHub, GitLab, git.gay, sourcehut, and Codeberg (see README.md for their links)
- Repo is mirrored to GitHub, GitLab, git.gay, git.girlcock.ceo, sourcehut, and Codeberg (see README.md for their links)
- Docker container images published to GitLab Container Registry, GitHub Container Registry, and Dockerhub
- Extensively revamp the example config to be extremely helpful and useful to both new users and power users
- Fixed every single clippy (default lints) and rustc warnings, including some that were performance related or potential safety issues / unsoundness
- Add a **lot** of other clippy and rustc lints and a rustfmt.toml file
@@ -121,6 +129,9 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
- Admin debug command to fetch a PDU from a remote server and inserts it into our database/timeline as backfill
- Add admin command to delete media via a specific MXC. This deletes the MXC from our database, and the file locally.
- Add admin commands for banning (blocking) room IDs from our local users joining (admins are always allowed) and evicts all our local users from that room, in addition to bulk room banning support, and blocks room invites (remote and local) to the banned room, as a moderation feature
- Add admin commands to output jemalloc memory stats and memory usage
- Add admin command to get conduwuit's uptime
- Add admin command to get rooms a *remote* user shares with us
## Misc:
@@ -138,5 +149,12 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
- Assume well-knowns are broken if they exceed past 10000 characters.
- Add support for the Matrix spec compliance test suite [Complement](https://github.com/matrix-org/complement/) via the Nix flake and various other fixes for it
- Add support for listening on both HTTP and HTTPS if using direct TLS with conduwuit for usecases such as Complement
- Implement running and diff'ing Complement results in CI
- Interest in supporting other operating systems such as macOS, BSDs, and Windows, and getting them added into CI and doing builds for them
- Add config option for disabling RocksDB Direct IO if needed
- Add various documentation on maintaining conduwuit, using RocksDB online backups, some troubleshooting, using admin commands, etc
- (Developers): Add support for [hot reloadable/"live" modular development](development/hot_reload.md)
- (Developers): Add support for tokio-console
- (Developers): Add support for tracing flame graphs
- Add `release-debuginfo` Cargo build profile
- No cryptocurrency donations allowed, conduwuit is fully maintained by independent queer maintainers, and with a strong priority on inclusitivity and comfort for protected groups 🏳️‍⚧️
+5 -1
View File
@@ -6,7 +6,7 @@
#### What's different about your fork than upstream Conduit?
See [differences.md](differences.md)
See the [differences](differences.md) page
#### How can I deploy my own?
@@ -14,4 +14,8 @@ See [differences.md](differences.md)
If you want to connect an Appservice to Conduwuit, take a look at the [appservices documentation](appservices.md).
#### How can I contribute?
See the [contributor's guide](contributing.md)
{{#include ../README.md:footer}}
+63
View File
@@ -0,0 +1,63 @@
# Maintaining your conduwuit setup
## Moderation
conduwuit has moderation through admin room commands. "binary commands" (medium priority) and an admin API (low priority) is planned. Some moderation-related config options are available in the example config such as "global ACLs" and blocking media requests to certain servers. See the example config for the moderation config options under the "Moderation / Privacy / Security" section.
conduwuit has moderation admin commands for:
- managing room aliases (`!admin rooms alias`)
- managing room directory (`!admin rooms directory`)
- managing room banning/blocking and user removal (`!admin rooms moderation`)
- managing user accounts (`!admin users`)
- fetching `/.well-known/matrix/support` from servers (`!admin federation`)
- blocking incoming federation for certain rooms (not the same as room banning) (`!admin federation`)
- deleting media (see [the media section](#media))
Any commands with `-list` in them will require a codeblock in the message with each object being newline delimited. An example of doing this is:
````
!admin rooms moderation ban-list-of-rooms
```
!roomid1:server.name
!roomid2:server.name
!roomid3:server.name
```
````
## Database
If using RocksDB, there's very little you need to do. Compaction is ran automatically based on various defined thresholds tuned for conduwuit to be high performance with the least I/O amplifcation or overhead. Manually running compaction is not recommended, or compaction via a timer. RocksDB is built with io_uring support via liburing for async read I/O.
Some RocksDB settings can be adjusted such as the compression method chosen. See the RocksDB section in the [example config](configuration.md). btrfs users may benefit from disabling compression on RocksDB if CoW is in use.
RocksDB troubleshooting can be found [in the RocksDB section of troubleshooting](troubleshooting.md).
## Backups
Currently only RocksDB supports online backups. If you'd like to backup your database online without any downtime, see the `!admin server` command for the backup commands and the `database_backup_path` config options in the example config. Please note that the format of the database backup is not the exact same. This is unfortunately a bad design choice by Facebook as we are using the database backup engine API from RocksDB, however the data is still there and can still be joined together.
To restore a backup from an online RocksDB backup:
- shutdown conduwuit
- create a new directory for merging together the data
- in the online backup created, copy all `.sst` files in `$DATABASE_BACKUP_PATH/shared_checksum` to your new directory
- trim all the strings so instead of `######_sxxxxxxxxx.sst`, it reads `######.sst`. A way of doing this with sed and bash is `for file in *.sst; do mv "$file" "$(echo "$file" | sed 's/_s.*/.sst/')"; done`
- copy all the files in `$DATABASE_BACKUP_PATH/1` to your new directory
- set your `database_path` config option to your new directory, or replace your old one with the new one you crafted
- start up conduwuit again and it should open as normal
If you'd like to do an offline backup, shutdown conduwuit and copy your `database_path` directory elsewhere. This can be restored with no modifications needed.
Backing up media is also just copying the `media/` directory from your database directory.
## Media
Media still needs various work, however conduwuit implements media deletion via:
- MXC URI
- Delete list of MXC URIs
- Delete remote media in the past `N` seconds/minutes
See the `!admin media` command for further information. All media in conduwuit is stored at `$DATABASE_DIR/media`. This will be configurable soon.
If you are finding yourself needing extensive granular control over media, we recommend looking into [Matrix Media Repo](https://github.com/t2bot/matrix-media-repo). conduwuit intends to implement various utilities for media, but MMR is dedicated to extensive media management.
Built-in S3 support is also planned, but for now using a "S3 filesystem" on `media/` works. conduwuit also sends a `Cache-Control` header of 1 year and immutable for all media requests (download and thumbnail) to reduce unnecessary media requests from browsers.
+62
View File
@@ -0,0 +1,62 @@
# Troubleshooting conduwuit
> ## Docker users ⚠️
>
> Docker is extremely UX unfriendly. Because of this, a ton of issues or support is actually Docker support, not conduwuit support. We also cannot document the ever-growing list of Docker issues here.
>
> If you intend on asking for support and you are using Docker, **PLEASE** triple validate your issues are **NOT** because you have a misconfiguration in your Docker setup.
>
> If there are things like Compose file issues or Dockerhub image issues, those can still be mentioned as long as they're something we can fix.
## Rocksdb / database issues
#### Direct IO
Some filesystems may not like RocksDB using [Direct IO](https://github.com/facebook/rocksdb/wiki/Direct-IO). Direct IO is for non-buffered I/O which improves conduwuit performance, but at least FUSE is a filesystem potentially known to not like this. See the [example config](configuration.md) for disabling it if needed. Issues from Direct IO on unsupported filesystems are usually shown as startup errors.
#### Database corruption
If your database is corrupted and is failing to start (e.g. checksum mismatch), it may be recoverable but careful steps must be taken, and there is no guarantee it may be recoverable.
RocksDB has the following recovery modes:
- `TolerateCorruptedTailRecords`
- `AbsoluteConsistency`
- `PointInTime`
- `SkipAnyCorruptedRecord`
By default, conduwuit uses `TolerateCorruptedTailRecords` as generally these may be due to bad federation and we can re-fetch the correct data over federation. The RocksDB default is `PointInTime` which will attempt to restore a "snapshot" of the data when it was last known to be good. This data can be either a few seconds old, or multiple minutes prior. `PointInTime` may not be suitable for default usage due to clients and servers possibly not being able to handle sudden "backwards time travels", and `AbsoluteConsistency` may be too strict.
`AbsoluteConsistency` will fail to start the database if any sign of corruption is detected. `SkipAnyCorruptedRecord` will skip all forms of corruption unless it forbids the database from opening (e.g. too severe). Usage of `SkipAnyCorruptedRecord` voids any support as this may cause more damage and/or leave your database in a permanently inconsistent state, but it may do something if `PointInTime` does not work as a last ditch effort.
With this in mind:
- First start conduwuit with the `PointInTime` recovery method. See the [example config](configuration.md) for how to do this using `rocksdb_recovery_mode`
- If your database successfully opens, clients are recommended to clear their client cache to account for the rollback
- Leave your conduwuit running in `PointInTime` for at least 30-60 minutes so as much possible corruption is restored
- If all goes will, you should be able to restore back to using `TolerateCorruptedTailRecords` and you have successfully recovered your database
## Media
#### "File name too long"
If you are running into the "file name is too long" OS error for media requests, your filesystem cannot handle file name lengths >=255 characters. This is unfortuntely due to Conduit (upstream) using base64 for file name keys which is very problematic for some filesystems as the base64 input is untrusted and long file names or specific inputs can cause this. If you would like to avoid this, you may build conduwuit yourself with the `sha256_media` feature. **This will lose database compatibility with upstream**.
## Debugging
Note that users should not really be debugging 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`.
#### Debug/Trace log level
conduwuit builds without debug or trace log levels by default for at least performance reasons. This may change in the future and/or binaries providing such configurations may be provided. If you need to access debug/trace log levels, you will need to build without the `release_max_log_level` feature.
#### Changing log level dynamically
conduwuit supports changing the tracing log environment filter on-the-fly using the admin command `!admin debug change-log-level`. This accepts a string **without quotes** the same format as the `log` config option.
#### Pinging servers
conduwuit can ping other servers using `!admin debug ping`. This takes a server name and goes through the server discovery process and queries `/_matrix/federation/v1/version`. Errors are outputted.
#### Allocator memory stats
When using jemalloc with jemallocator's `stats` feature, you can see conduwuit's jemalloc memory stats by using `!admin debug memory-stats`
+46 -16
View File
@@ -1,25 +1,55 @@
# Setting up TURN/STURN
## General instructions
In order to make or receive calls, a TURN server is required. conduwuit suggests using [Coturn](https://github.com/coturn/coturn) for this purpose, which is also available as a Docker image.
* It is assumed you have a [Coturn server](https://github.com/coturn/coturn) up and running. See [Synapse reference implementation](https://github.com/matrix-org/synapse/blob/develop/docs/turn-howto.md).
### Configuration
## Edit/Add a few settings to your existing conduit.toml
Create a configuration file called `coturn.conf` containing:
```conf
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 conduwuit. You can either modify conduwuit.toml to include these lines:
```
# Refer to your Coturn settings.
# `your.turn.url` has to match the REALM setting of your Coturn as well as `transport`.
turn_uris = ["turn:your.turn.url?transport=udp", "turn:your.turn.url?transport=tcp"]
# static-auth-secret of your turnserver
turn_secret = "ADD SECRET HERE"
# If you have your TURN server configured to use a username and password
# you can provide these information too. In this case comment out `turn_secret above`!
#turn_username = ""
#turn_password = ""
turn_uris = ["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]
turn_secret = "<secret key from coturn configuration>"
```
## Apply settings
or append the following to the docker environment variables dependig on which configuration method you used earlier:
Restart Conduit.
```yml
CONDUIT_TURN_URIS: '["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]'
CONDUIT_TURN_SECRET: "<secret key from coturn configuration>"
```
Restart conduwuit to apply these changes.
### 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).
+62 -4
View File
@@ -58,7 +58,7 @@ script = "lychee --version"
[[task]]
name = "cargo-audit"
group = "security"
script = "cargo audit -D warnings -D unmaintained -D unsound -D yanked --ignore RUSTSEC-2020-0016"
script = "cargo audit -D warnings -D unmaintained -D unsound -D yanked"
[[task]]
name = "cargo-fmt"
@@ -78,14 +78,60 @@ RUSTDOCFLAGS="-D warnings" cargo doc \
"""
[[task]]
name = "cargo-clippy"
name = "clippy/default"
group = "lints"
script = "cargo clippy --workspace --all-targets --all-features --color=always -- -D warnings"
script = """
cargo clippy \
--workspace \
--all-targets \
--color=always \
-- \
-D warnings
"""
[[task]]
name = "clippy/all"
group = "lints"
script = """
cargo clippy \
--workspace \
--all-targets \
--all-features \
--color=always \
-- \
-D warnings
"""
[[task]]
name = "clippy/jemalloc"
group = "lints"
script = """
cargo clippy \
--workspace \
--features jemalloc \
--all-targets \
--color=always \
-- \
-D warnings
"""
[[task]]
name = "clippy/hardened_malloc"
group = "lints"
script = """
cargo clippy \
--workspace \
--features hardened_malloc \
--all-targets \
--color=always \
-- \
-D warnings
"""
[[task]]
name = "lychee"
group = "lints"
script = "lychee --offline docs"
script = "lychee --verbose --offline docs *.md"
[[task]]
name = "cargo"
@@ -99,3 +145,15 @@ cargo test \
-- \
--color=always
"""
# Ensure that the flake's default output can build and run without crashing
#
# This is a dynamically-linked jemalloc build, which is a case not covered by
# our other tests. We've had linking problems in the past with dynamic
# jemalloc builds that usually show up as an immediate segfault or "invalid free"
[[task]]
name = "nix-default"
group = "tests"
script = """
nix run .#default -- --help
"""
Generated
+44 -21
View File
@@ -26,15 +26,16 @@
"complement": {
"flake": false,
"locked": {
"lastModified": 1713458251,
"narHash": "sha256-hom/Lt0gZzLWqFhUJG0X2i88CAMIILInO5w0tPj6G3s=",
"lastModified": 1715700731,
"narHash": "sha256-cie+b5N/TQAFD8vF/XbqfyFJkFU0qUPDbtJQDm/TfQc=",
"owner": "matrix-org",
"repo": "complement",
"rev": "d73c81a091604b0fc5b6b0617dcac58c25763f57",
"rev": "8587fb3cbe746754b2c883ff6c818ca4d987d0a5",
"type": "github"
},
"original": {
"owner": "matrix-org",
"ref": "main",
"repo": "complement",
"type": "github"
}
@@ -67,11 +68,11 @@
]
},
"locked": {
"lastModified": 1713738183,
"narHash": "sha256-qd/MuLm7OfKQKyd4FAMqV4H6zYyOfef5lLzRrmXwKJM=",
"lastModified": 1716569590,
"narHash": "sha256-5eDbq8TuXFGGO3mqJFzhUbt5zHVTf5zilQoyW5jnJwo=",
"owner": "ipetkov",
"repo": "crane",
"rev": "f6c6a2fb1b8bd9b65d65ca9342dd0eb180a63f11",
"rev": "109987da061a1bf452f435f1653c47511587d919",
"type": "github"
},
"original": {
@@ -89,15 +90,16 @@
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1713680591,
"narHash": "sha256-3pbv7UgAgetwz9YdjzIT/lZ6Rgj6wj6MR4mphBLyDjU=",
"lastModified": 1716359173,
"narHash": "sha256-pYcjP6Gy7i6jPWrjiWAVV0BCQp+DdmGaI/k65lBb/kM=",
"owner": "nix-community",
"repo": "fenix",
"rev": "19aaa94a73cc670a4d87e84f0909966cd8f8cd79",
"rev": "b6fc5035b28e36a98370d0eac44f4ef3fd323df6",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "main",
"repo": "fenix",
"type": "github"
}
@@ -130,6 +132,7 @@
},
"original": {
"owner": "edolstra",
"ref": "master",
"repo": "flake-compat",
"type": "github"
}
@@ -163,10 +166,28 @@
},
"original": {
"owner": "numtide",
"ref": "main",
"repo": "flake-utils",
"type": "github"
}
},
"liburing": {
"flake": false,
"locked": {
"lastModified": 1716565485,
"narHash": "sha256-4R19aJNQYs6vb0/Hz4bWT56YN1P1DkFL/sxdE4Yj0CE=",
"owner": "axboe",
"repo": "liburing",
"rev": "b90c0e670a93caabbebe2d9e24ff85cece4cfe0e",
"type": "github"
},
"original": {
"owner": "axboe",
"ref": "master",
"repo": "liburing",
"type": "github"
}
},
"nix-filter": {
"locked": {
"lastModified": 1710156097,
@@ -178,6 +199,7 @@
},
"original": {
"owner": "numtide",
"ref": "main",
"repo": "nix-filter",
"type": "github"
}
@@ -216,11 +238,11 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1713537308,
"narHash": "sha256-XtTSSIB2DA6tOv+l0FhvfDMiyCmhoRbNB+0SeInZkbk=",
"lastModified": 1716330097,
"narHash": "sha256-8BO3B7e3BiyIDsaKA0tY8O88rClYRTjvAp66y+VBUeU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5c24cf2f0a12ad855f444c30b2421d044120c66f",
"rev": "5710852ba686cc1fd0d3b8e22b3117d43ba374c2",
"type": "github"
},
"original": {
@@ -233,16 +255,16 @@
"rocksdb": {
"flake": false,
"locked": {
"lastModified": 1713810944,
"narHash": "sha256-/Xf0bzNJPclH9IP80QNaABfhj4IAR5LycYET18VFCXc=",
"owner": "facebook",
"lastModified": 1716773462,
"narHash": "sha256-5kUH+XK+2lbFfUgbxuNy3YMLHbp6scfWPdtc8za1wDM=",
"owner": "girlbossceo",
"repo": "rocksdb",
"rev": "6f7cabeac80a3a6150be2c8a8369fcecb107bf43",
"rev": "c8a1450231e9c608edf535538dbe8ca1a8d2f3bc",
"type": "github"
},
"original": {
"owner": "facebook",
"ref": "v9.1.1",
"owner": "girlbossceo",
"ref": "v9.2.1",
"repo": "rocksdb",
"type": "github"
}
@@ -255,6 +277,7 @@
"fenix": "fenix",
"flake-compat": "flake-compat_2",
"flake-utils": "flake-utils_2",
"liburing": "liburing",
"nix-filter": "nix-filter",
"nixpkgs": "nixpkgs_2",
"rocksdb": "rocksdb"
@@ -263,11 +286,11 @@
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1713628977,
"narHash": "sha256-iN5QUlUq527lswmBC+RopfXdu6Xx7mmTaBSH2l59FtM=",
"lastModified": 1716107283,
"narHash": "sha256-NJgrwLiLGHDrCia5AeIvZUHUY7xYGVryee0/9D3Ir1I=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "55d9a533b309119c8acd13061581b43ae8840823",
"rev": "21ec8f523812b88418b2bfc64240c62b3dd967bd",
"type": "github"
},
"original": {
+96 -79
View File
@@ -1,30 +1,34 @@
{
inputs = {
attic.url = "github:zhaofengli/attic?ref=main";
complement = { url = "github:matrix-org/complement"; flake = false; };
complement = { url = "github:matrix-org/complement?ref=main"; flake = false; };
crane = { url = "github:ipetkov/crane?ref=master"; inputs.nixpkgs.follows = "nixpkgs"; };
fenix = { url = "github:nix-community/fenix"; inputs.nixpkgs.follows = "nixpkgs"; };
flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
flake-utils.url = "github:numtide/flake-utils";
nix-filter.url = "github:numtide/nix-filter";
fenix = { url = "github:nix-community/fenix?ref=main"; inputs.nixpkgs.follows = "nixpkgs"; };
flake-compat = { url = "github:edolstra/flake-compat?ref=master"; flake = false; };
flake-utils.url = "github:numtide/flake-utils?ref=main";
nix-filter.url = "github:numtide/nix-filter?ref=main";
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
rocksdb = { url = "github:facebook/rocksdb?ref=v9.1.1"; flake = false; };
# https://github.com/girlbossceo/rocksdb/commit/db6df0b185774778457dabfcbd822cb81760cade
rocksdb = { url = "github:girlbossceo/rocksdb?ref=v9.2.1"; flake = false; };
liburing = { url = "github:axboe/liburing?ref=master"; flake = false; };
};
outputs = inputs:
inputs.flake-utils.lib.eachDefaultSystem (system:
let
pkgsHost = inputs.nixpkgs.legacyPackages.${system};
pkgsHostStatic = pkgsHost.pkgsStatic;
# The Rust toolchain to use
toolchain = inputs.fenix.packages.${system}.fromToolchainFile {
file = ./rust-toolchain.toml;
# See also `rust-toolchain.toml`
sha256 = "sha256-SXRtAuO4IqNOQq+nLbrsDFbVk+3aVA8NNpSZsKlVH/8=";
sha256 = "sha256-+syqAd2kX8KVa8/U2gz3blIQTTsYYt3U63xBWaGOSc8";
};
scope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: {
mkScope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: {
inherit pkgs;
book = self.callPackage ./nix/pkgs/book {};
complement = self.callPackage ./nix/pkgs/complement {};
craneLib = ((inputs.crane.mkLib pkgs).overrideToolchain toolchain);
@@ -38,22 +42,87 @@
(builtins.fromJSON (builtins.readFile ./flake.lock))
.nodes.rocksdb.original.ref;
});
# TODO: remove once https://github.com/NixOS/nixpkgs/pull/314945 is available
liburing = pkgs.liburing.overrideAttrs (old: {
# the configure script doesn't support these, and unconditionally
# builds both static and dynamic libraries.
configureFlags = pkgs.lib.subtractLists
[ "--enable-static" "--disable-shared" ]
old.configureFlags;
postInstall = old.postInstall + ''
# we remove the extra outputs
#
# we need to do this to prevent rocksdb from trying to link the
# static library in a dynamic stdenv
rm $out/lib/liburing*${
if pkgs.stdenv.hostPlatform.isStatic then ".so*" else ".a"
}
'';
});
});
scopeHost = (scope pkgsHost);
scopeHost = mkScope pkgsHost;
scopeHostStatic = mkScope pkgsHostStatic;
mkDevShell = scope: scope.pkgs.mkShell {
env = scope.main.env // {
# Rust Analyzer needs to be able to find the path to default crate
# sources, and it can read this environment variable to do so. The
# `rust-src` component is required in order for this to work.
RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library";
# Convenient way to access a pinned version of Complement's source
# code.
COMPLEMENT_SRC = inputs.complement.outPath;
# Needed for Complement
CGO_CFLAGS = "-I${scope.pkgs.olm}/include";
CGO_LDFLAGS = "-L${scope.pkgs.olm}/lib";
};
# Development tools
packages = [
# Always use nightly rustfmt because most of its options are unstable
#
# This needs to come before `toolchain` in this list, otherwise
# `$PATH` will have stable rustfmt instead.
inputs.fenix.packages.${system}.latest.rustfmt
toolchain
]
++ (with pkgsHost.pkgs; [
engage
cargo-audit
# Needed for producing Debian packages
cargo-deb
# Needed for Complement
go
# Needed for our script for Complement
jq
# Needed for finding broken markdown links
lychee
# Useful for editing the book locally
mdbook
])
++ scope.main.buildInputs
++ scope.main.propagatedBuildInputs
++ scope.main.nativeBuildInputs;
meta.broken = scope.main.meta.broken;
};
in
{
packages = {
default = scopeHost.main;
jemalloc = scopeHost.main.override { features = ["jemalloc"]; };
hmalloc = scopeHost.main.override { features = ["hardened_malloc"]; };
oci-image = scopeHost.oci-image;
oci-image-jemalloc = scopeHost.oci-image.override {
main = scopeHost.main.override {
features = ["jemalloc"];
};
};
oci-image-hmalloc = scopeHost.oci-image.override {
main = scopeHost.main.override {
features = ["hardened_malloc"];
@@ -63,6 +132,7 @@
book = scopeHost.book;
complement = scopeHost.complement;
static-complement = scopeHostStatic.complement;
}
//
builtins.listToAttrs
@@ -78,7 +148,7 @@
config = crossSystem;
};
}).pkgsStatic;
scopeCrossStatic = scope pkgsCrossStatic;
scopeCrossStatic = mkScope pkgsCrossStatic;
in
[
# An output for a statically-linked binary
@@ -87,14 +157,6 @@
value = scopeCrossStatic.main;
}
# An output for a statically-linked binary with jemalloc
{
name = "${binaryName}-jemalloc";
value = scopeCrossStatic.main.override {
features = ["jemalloc"];
};
}
# An output for a statically-linked binary with hardened_malloc
{
name = "${binaryName}-hmalloc";
@@ -109,16 +171,6 @@
value = scopeCrossStatic.oci-image;
}
# An output for an OCI image based on that binary with jemalloc
{
name = "oci-image-${crossSystem}-jemalloc";
value = scopeCrossStatic.oci-image.override {
main = scopeCrossStatic.main.override {
features = ["jemalloc"];
};
};
}
# An output for an OCI image based on that binary with hardened_malloc
{
name = "oci-image-${crossSystem}-hmalloc";
@@ -137,50 +189,15 @@
)
);
devShells.default = pkgsHost.mkShell {
env = scopeHost.main.env // {
# Rust Analyzer needs to be able to find the path to default crate
# sources, and it can read this environment variable to do so. The
# `rust-src` component is required in order for this to work.
RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library";
# Convenient way to access a pinned version of Complement's source
# code.
COMPLEMENT_SRC = inputs.complement.outPath;
};
# Development tools
packages = [
# Always use nightly rustfmt because most of its options are unstable
#
# This needs to come before `toolchain` in this list, otherwise
# `$PATH` will have stable rustfmt instead.
inputs.fenix.packages.${system}.latest.rustfmt
toolchain
]
++ (with pkgsHost; [
engage
cargo-audit
# Needed for producing Debian packages
cargo-deb
# Needed for Complement
go
olm
# Needed for our script for Complement
jq
# Needed for finding broken markdown links
lychee
# Useful for editing the book locally
mdbook
])
++
scopeHost.main.nativeBuildInputs;
};
devShells.default = mkDevShell scopeHostStatic;
devShells.all-features = mkDevShell
(scopeHostStatic.overrideScope (final: prev: {
main = prev.main.override { all_features = true; };
}));
devShells.no-features = mkDevShell
(scopeHostStatic.overrideScope (final: prev: {
main = prev.main.override { default_features = false; };
}));
devShells.dynamic = mkDevShell scopeHost;
});
}
+2
View File
@@ -14,7 +14,9 @@ stdenv.mkDerivation {
include = [
"book.toml"
"conduwuit-example.toml"
"CONTRIBUTING.md"
"README.md"
"debian/conduwuit.service"
"debian/README.md"
"docs"
];
+3 -5
View File
@@ -44,16 +44,14 @@ let
-sha256
${lib.getExe' coreutils "env"} \
CONDUIT_SERVER_NAME="$SERVER_NAME" \
CONDUIT_WELL_KNOWN_SERVER="$SERVER_NAME:8448" \
CONDUIT_WELL_KNOWN_SERVER="$SERVER_NAME:8008" \
CONDUWUIT_SERVER_NAME="$SERVER_NAME" \
${lib.getExe main'}
'';
in
dockerTools.buildImage {
name = "complement-${main.pname}";
tag = "dev";
tag = "main";
copyToRoot = buildEnv {
name = "root";
@@ -81,7 +79,7 @@ dockerTools.buildImage {
Env = [
"SSL_CERT_FILE=/complement/ca/ca.crt"
"CONDUIT_CONFIG=${./config.toml}"
"CONDUWUIT_CONFIG=${./config.toml}"
];
ExposedPorts = {
+161 -55
View File
@@ -1,75 +1,181 @@
{ inputs
# Dependencies
, craneLib
# Dependencies (keep sorted)
{ craneLib
, inputs
, jq
, lib
, libiconv
, liburing
, pkgsBuildHost
, rocksdb
, rust
, rust-jemalloc-sys
, stdenv
# Options
# Options (keep sorted)
, default_features ? true
, disable_release_max_log_level ? false
, all_features ? false
, disable_features ? []
, features ? []
, profile ? "release"
}:
craneLib.buildPackage rec {
src = inputs.nix-filter {
root = inputs.self;
include = [
"src"
"Cargo.toml"
"Cargo.lock"
let
# We perform default-feature unification in nix, because some of the dependencies
# on the nix side depend on feature values.
workspaceMembers = builtins.map (member: "${inputs.self}/src/${member}")
(builtins.attrNames (builtins.readDir "${inputs.self}/src"));
crateFeatures = path:
let manifest = lib.importTOML "${path}/Cargo.toml"; in
lib.remove "default" (lib.attrNames manifest.features) ++
lib.attrNames
(lib.filterAttrs
(_: dependency: dependency.optional or false)
manifest.dependencies);
crateDefaultFeatures = path:
(lib.importTOML "${path}/Cargo.toml").features.default;
allDefaultFeatures = lib.unique
(lib.flatten (builtins.map crateDefaultFeatures workspaceMembers));
allFeatures = lib.unique
(lib.flatten (builtins.map crateFeatures workspaceMembers));
features' = lib.unique
(features ++
lib.optionals default_features allDefaultFeatures ++
lib.optionals all_features allFeatures);
disable_features' = disable_features ++ lib.optionals disable_release_max_log_level ["release_max_log_level"];
features'' = lib.subtractLists disable_features' features';
featureEnabled = feature : builtins.elem feature features'';
enableLiburing = featureEnabled "io_uring" && stdenv.isLinux;
# This derivation will set the JEMALLOC_OVERRIDE variable, causing the
# tikv-jemalloc-sys crate to use the nixpkgs jemalloc instead of building it's
# own. In order for this to work, we need to set flags on the build that match
# whatever flags tikv-jemalloc-sys was going to use. These are dependent on
# which features we enable in tikv-jemalloc-sys.
rust-jemalloc-sys' = (rust-jemalloc-sys.override {
# tikv-jemalloc-sys/unprefixed_malloc_on_supported_platforms feature
unprefixed = true;
}).overrideAttrs (old: {
configureFlags = old.configureFlags ++
# tikv-jemalloc-sys/profiling feature
lib.optional (featureEnabled "jemalloc_prof") "--enable-prof";
});
buildDepsOnlyEnv =
let
rocksdb' = (rocksdb.override {
jemalloc = rust-jemalloc-sys';
# rocksdb fails to build with prefixed jemalloc, which is required on
# darwin due to [1]. In this case, fall back to building rocksdb with
# libc malloc. This should not cause conflicts, because all of the
# jemalloc symbols are prefixed.
#
# [1]: https://github.com/tikv/jemallocator/blob/ab0676d77e81268cd09b059260c75b38dbef2d51/jemalloc-sys/src/env.rs#L17
enableJemalloc = featureEnabled "jemalloc" && !stdenv.isDarwin;
}).overrideAttrs (old: {
# TODO: static rocksdb fails to build on darwin
# build log at <https://girlboss.ceo/~strawberry/pb/JjGH>
meta.broken = stdenv.hostPlatform.isStatic && stdenv.isDarwin;
# TODO: switch to enableUring option once https://github.com/NixOS/nixpkgs/pull/314945 is available
buildInputs = old.buildInputs ++ lib.optional enableLiburing liburing;
});
in
{
# https://crane.dev/faq/rebuilds-bindgen.html
NIX_OUTPATH_USED_AS_RANDOM_SEED = "aaaaaaaaaa";
CARGO_PROFILE = profile;
ROCKSDB_INCLUDE_DIR = "${rocksdb'}/include";
ROCKSDB_LIB_DIR = "${rocksdb'}/lib";
}
//
(import ./cross-compilation-env.nix {
# Keep sorted
inherit
lib
pkgsBuildHost
rust
stdenv;
});
buildPackageEnv = {
CONDUWUIT_VERSION_EXTRA = inputs.self.shortRev or inputs.self.dirtyShortRev or "";
} // buildDepsOnlyEnv // {
# Only needed in static stdenv because these are transitive dependencies of rocksdb
CARGO_BUILD_RUSTFLAGS = buildDepsOnlyEnv.CARGO_BUILD_RUSTFLAGS
+ lib.optionalString (enableLiburing && stdenv.hostPlatform.isStatic)
" -L${lib.getLib liburing}/lib -luring";
};
commonAttrs = {
inherit
(craneLib.crateNameFromCargoToml {
cargoToml = "${inputs.self}/Cargo.toml";
})
pname
version;
src = let filter = inputs.nix-filter.lib; in filter {
root = inputs.self;
# Keep sorted
include = [
"Cargo.lock"
"Cargo.toml"
"deps"
"src"
];
};
buildInputs = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys';
nativeBuildInputs = [
# bindgen needs the build platform's libclang. Apparently due to "splicing
# weirdness", pkgs.rustPlatform.bindgenHook on its own doesn't quite do the
# right thing here.
pkgsBuildHost.rustPlatform.bindgenHook
# We don't actually depend on `jq`, but crane's `buildPackage` does, but
# its `buildDepsOnly` doesn't. This causes those two derivations to have
# differing values for `NIX_CFLAGS_COMPILE`, which contributes to spurious
# rebuilds of bindgen and its depedents.
jq
]
++ lib.optionals stdenv.isDarwin [
# https://github.com/NixOS/nixpkgs/issues/206242
libiconv
# https://stackoverflow.com/questions/69869574/properly-adding-darwin-apple-sdk-to-a-nix-shell
# https://discourse.nixos.org/t/compile-a-rust-binary-on-macos-dbcrossbar/8612
pkgsBuildHost.darwin.apple_sdk.frameworks.Security
];
};
};
in
craneLib.buildPackage ( commonAttrs // {
cargoArtifacts = craneLib.buildDepsOnly (commonAttrs // {
env = buildDepsOnlyEnv;
});
cargoExtraArgs = "--no-default-features "
+ lib.optionalString
(features'' != [])
"--features " + (builtins.concatStringsSep "," features'');
# This is redundant with CI
cargoTestCommand = "";
cargoCheckCommand = "";
doCheck = false;
env =
let
rocksdb' = rocksdb.override {
enableJemalloc = builtins.elem "jemalloc" features;
};
in
{
CARGO_PROFILE = profile;
CONDUIT_VERSION_EXTRA = inputs.self.shortRev or inputs.self.dirtyShortRev;
ROCKSDB_INCLUDE_DIR = "${rocksdb'}/include";
ROCKSDB_LIB_DIR = "${rocksdb'}/lib";
}
//
(import ./cross-compilation-env.nix {
inherit
lib
pkgsBuildHost
rust
stdenv;
});
nativeBuildInputs = [
# bindgen needs the build platform's libclang. Apparently due to "splicing
# weirdness", pkgs.rustPlatform.bindgenHook on its own doesn't quite do the
# right thing here.
pkgsBuildHost.rustPlatform.bindgenHook
]
++ lib.optionals stdenv.isDarwin [ libiconv ];
cargoExtraArgs = ""
+ lib.optionalString
(!default_features)
"--no-default-features "
+ lib.optionalString
(features != [])
"--features " + (builtins.concatStringsSep "," features);
meta.mainProgram = (craneLib.crateNameFromCargoToml {
cargoToml = "${inputs.self}/Cargo.toml";
}).pname;
env = buildPackageEnv;
passthru = {
inherit env;
env = buildPackageEnv;
};
}
meta.mainProgram = commonAttrs.pname;
})
+2 -1
View File
@@ -11,5 +11,6 @@
},
"nix": {
"enabled": true
}
},
"labels": ["dependencies", "github_actions"]
}
+2 -2
View File
@@ -11,7 +11,7 @@
# If you're having trouble making the relevant changes, bug a maintainer.
[toolchain]
channel = "1.75.0"
channel = "1.77.0"
components = [
# For rust-analyzer
"rust-src",
@@ -20,4 +20,4 @@ targets = [
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-musl",
"aarch64-unknown-linux-musl",
]
]
+63
View File
@@ -0,0 +1,63 @@
[package]
name = "conduit_admin"
version.workspace = true
edition.workspace = true
[lib]
path = "mod.rs"
crate-type = [
"rlib",
# "dylib",
]
[features]
default = [
"rocksdb",
"io_uring",
"jemalloc",
"zstd_compression",
"release_max_log_level",
]
dev_release_log_level = []
release_max_log_level = [
"tracing/max_level_trace",
"tracing/release_max_level_info",
"log/max_level_trace",
"log/release_max_level_info",
]
rocksdb = [
"dep:rust-rocksdb",
]
jemalloc = [
"rust-rocksdb/jemalloc",
]
io_uring = [
"rust-rocksdb/io-uring",
]
zstd_compression = [
"rust-rocksdb/zstd",
]
[dependencies]
clap.workspace = true
conduit-api.workspace = true
conduit-core.workspace = true
conduit-database.workspace = true
conduit-service.workspace = true
futures-util.workspace = true
log.workspace = true
loole.workspace = true
regex.workspace = true
ruma.workspace = true
rust-rocksdb.optional = true
rust-rocksdb.workspace = true
serde_json.workspace = true
serde.workspace = true
serde_yaml.workspace = true
tokio.workspace = true
tracing-subscriber.workspace = true
tracing.workspace = true
[lints]
workspace = true
@@ -1,10 +1,10 @@
use ruma::{api::appservice::Registration, events::room::message::RoomMessageEventContent};
use crate::{service::admin::escape_html, services, Result};
use crate::{escape_html, services, Result};
pub(crate) async fn register(body: Vec<&str>) -> Result<RoomMessageEventContent> {
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" {
let appservice_config = body[1..body.len() - 1].join("\n");
let appservice_config = body[1..body.len().checked_sub(1).unwrap()].join("\n");
let parsed_config = serde_yaml::from_str::<Registration>(&appservice_config);
match parsed_config {
Ok(yaml) => match services().appservice.register_appservice(yaml).await {
@@ -1,8 +1,8 @@
use clap::Subcommand;
use conduit::Result;
use ruma::events::room::message::RoomMessageEventContent;
use self::appservice_command::{list, register, show, unregister};
use crate::Result;
pub(crate) mod appservice_command;
@@ -1,15 +1,15 @@
use std::{collections::BTreeMap, sync::Arc, time::Instant};
use conduit::{utils::HtmlEscape, Error, Result};
use ruma::{
api::client::error::ErrorKind, events::room::message::RoomMessageEventContent, CanonicalJsonObject, EventId,
RoomId, RoomVersionId, ServerName,
};
use service::{rooms::event_handler::parse_incoming_pdu, sending::send::resolve_actual_dest, services, PduEvent};
use tokio::sync::RwLock;
use tracing::{debug, info, warn};
use tracing_subscriber::EnvFilter;
use crate::{api::server_server::parse_incoming_pdu, services, utils::HtmlEscape, Error, PduEvent, Result};
pub(crate) async fn get_auth_chain(_body: Vec<&str>, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
let event_id = Arc::<EventId>::from(event_id);
if let Some(event) = services().rooms.timeline.get_pdu_json(&event_id)? {
@@ -118,13 +118,15 @@ pub(crate) async fn get_remote_pdu_list(
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" {
let list = body
.clone()
.drain(1..body.len() - 1)
.drain(1..body.len().checked_sub(1).unwrap())
.filter_map(|pdu| EventId::parse(pdu).ok())
.collect::<Vec<_>>();
for pdu in list {
if force {
_ = get_remote_pdu(Vec::new(), Box::from(pdu), server.clone()).await;
if let Err(e) = get_remote_pdu(Vec::new(), Box::from(pdu), server.clone()).await {
warn!(%e, "Failed to get remote PDU, ignoring error");
}
} else {
get_remote_pdu(Vec::new(), Box::from(pdu), server.clone()).await?;
}
@@ -329,9 +331,9 @@ pub(crate) async fn change_log_level(
};
match services()
.globals
.server
.tracing_reload_handle
.modify(|filter| *filter = old_filter_layer)
.reload(&old_filter_layer)
{
Ok(()) => {
return Ok(RoomMessageEventContent::text_plain(format!(
@@ -358,9 +360,9 @@ pub(crate) async fn change_log_level(
};
match services()
.globals
.server
.tracing_reload_handle
.modify(|filter| *filter = new_filter_layer)
.reload(&new_filter_layer)
{
Ok(()) => {
return Ok(RoomMessageEventContent::text_plain("Successfully changed log level"));
@@ -378,7 +380,7 @@ pub(crate) async fn change_log_level(
pub(crate) async fn sign_json(body: Vec<&str>) -> Result<RoomMessageEventContent> {
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" {
let string = body[1..body.len() - 1].join("\n");
let string = body[1..body.len().checked_sub(1).unwrap()].join("\n");
match serde_json::from_str(&string) {
Ok(mut value) => {
ruma::signatures::sign_json(
@@ -401,7 +403,7 @@ pub(crate) async fn sign_json(body: Vec<&str>) -> Result<RoomMessageEventContent
pub(crate) async fn verify_json(body: Vec<&str>) -> Result<RoomMessageEventContent> {
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" {
let string = body[1..body.len() - 1].join("\n");
let string = body[1..body.len().checked_sub(1).unwrap()].join("\n");
match serde_json::from_str(&string) {
Ok(value) => {
let pub_key_map = RwLock::new(BTreeMap::new());
@@ -428,3 +430,39 @@ pub(crate) async fn verify_json(body: Vec<&str>) -> Result<RoomMessageEventConte
))
}
}
pub(crate) async fn resolve_true_destination(
_body: Vec<&str>, server_name: Box<ServerName>, no_cache: bool,
) -> Result<RoomMessageEventContent> {
if !services().globals.config.allow_federation {
return Ok(RoomMessageEventContent::text_plain(
"Federation is disabled on this homeserver.",
));
}
if server_name == services().globals.config.server_name {
return Ok(RoomMessageEventContent::text_plain(
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs.",
));
}
let (actual_dest, hostname_uri) = resolve_actual_dest(&server_name, no_cache).await?;
Ok(RoomMessageEventContent::text_plain(format!(
"Actual destination: {actual_dest:?} | Hostname URI: {hostname_uri}"
)))
}
#[must_use]
pub(crate) fn memory_stats() -> RoomMessageEventContent {
let html_body = conduit::alloc::memory_stats();
if html_body.is_empty() {
return RoomMessageEventContent::text_plain("malloc stats are not supported on your compiled malloc.");
}
RoomMessageEventContent::text_html(
"This command's output can only be viewed by clients that render HTML.".to_owned(),
html_body,
)
}
@@ -3,7 +3,7 @@ use ruma::{events::room::message::RoomMessageEventContent, EventId, RoomId, Serv
use self::debug_commands::{
change_log_level, force_device_list_updates, get_auth_chain, get_pdu, get_remote_pdu, get_remote_pdu_list,
get_room_state, parse_pdu, ping, sign_json, verify_json,
get_room_state, memory_stats, parse_pdu, ping, resolve_true_destination, sign_json, verify_json,
};
use crate::Result;
@@ -106,6 +106,20 @@ pub(crate) enum DebugCommand {
/// This command needs a JSON blob provided in a Markdown code block below
/// the command.
VerifyJson,
/// - Runs a server name through conduwuit's true destination resolution
/// process
///
/// Useful for debugging well-known issues
ResolveTrueDestination {
server_name: Box<ServerName>,
#[arg(short, long)]
no_cache: bool,
},
/// - Print extended memory usage
MemoryStats,
}
pub(crate) async fn process(command: DebugCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
@@ -138,5 +152,10 @@ pub(crate) async fn process(command: DebugCommand, body: Vec<&str>) -> Result<Ro
server,
force,
} => get_remote_pdu_list(body, server, force).await?,
DebugCommand::ResolveTrueDestination {
server_name,
no_cache,
} => resolve_true_destination(body, server_name, no_cache).await?,
DebugCommand::MemoryStats => memory_stats(),
})
}
@@ -1,8 +1,8 @@
use std::fmt::Write as _;
use std::fmt::Write;
use ruma::{events::room::message::RoomMessageEventContent, RoomId, ServerName};
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomId, ServerName, UserId};
use crate::{services, utils::HtmlEscape, Result};
use crate::{escape_html, get_room_info, services, utils::HtmlEscape, Result};
pub(crate) async fn disable_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
services().rooms.metadata.disable_room(&room_id, true)?;
@@ -20,7 +20,8 @@ pub(crate) async fn incoming_federeation(_body: Vec<&str>) -> Result<RoomMessage
for (r, (e, i)) in map.iter() {
let elapsed = i.elapsed();
let _ = writeln!(msg, "{} {}: {}m{}s", r, e, elapsed.as_secs() / 60, elapsed.as_secs() % 60);
writeln!(msg, "{} {}: {}m{}s", r, e, elapsed.as_secs() / 60, elapsed.as_secs() % 60,)
.expect("should be able to write to string buffer");
}
Ok(RoomMessageEventContent::text_plain(&msg))
}
@@ -70,3 +71,60 @@ pub(crate) async fn fetch_support_well_known(
),
))
}
pub(crate) async fn remote_user_in_rooms(_body: Vec<&str>, user_id: Box<UserId>) -> Result<RoomMessageEventContent> {
if user_id.server_name() == services().globals.config.server_name {
return Ok(RoomMessageEventContent::text_plain(
"User belongs to our server, please use `list-joined-rooms` user admin command instead.",
));
}
if !services().users.exists(&user_id)? {
return Ok(RoomMessageEventContent::text_plain(
"Remote user does not exist in our database.",
));
}
let mut rooms: Vec<(OwnedRoomId, u64, String)> = services()
.rooms
.state_cache
.rooms_joined(&user_id)
.filter_map(Result::ok)
.map(|room_id| get_room_info(&room_id))
.collect();
if rooms.is_empty() {
return Ok(RoomMessageEventContent::text_plain("User is not in any rooms."));
}
rooms.sort_by_key(|r| r.1);
rooms.reverse();
let output_plain = format!(
"Rooms {user_id} shares with us:\n{}",
rooms
.iter()
.map(|(id, members, name)| format!("{id}\tMembers: {members}\tName: {name}"))
.collect::<Vec<_>>()
.join("\n")
);
let output_html = format!(
"<table><caption>Rooms {user_id} shares with \
us</caption>\n<tr><th>id</th>\t<th>members</th>\t<th>name</th></tr>\n{}</table>",
rooms
.iter()
.fold(String::new(), |mut output, (id, members, name)| {
writeln!(
output,
"<tr><td>{}</td>\t<td>{}</td>\t<td>{}</td></tr>",
escape_html(id.as_ref()),
members,
escape_html(name)
)
.expect("should be able to write to string buffer");
output
})
);
Ok(RoomMessageEventContent::text_html(output_plain, output_html))
}
@@ -1,7 +1,9 @@
use clap::Subcommand;
use ruma::{events::room::message::RoomMessageEventContent, RoomId, ServerName};
use ruma::{events::room::message::RoomMessageEventContent, RoomId, ServerName, UserId};
use self::federation_commands::{disable_room, enable_room, fetch_support_well_known, incoming_federeation};
use self::federation_commands::{
disable_room, enable_room, fetch_support_well_known, incoming_federeation, remote_user_in_rooms,
};
use crate::Result;
pub(crate) mod federation_commands;
@@ -34,6 +36,11 @@ pub(crate) enum FederationCommand {
FetchSupportWellKnown {
server_name: Box<ServerName>,
},
/// - Lists all the rooms we share/track with the specified *remote* user
RemoteUserInRooms {
user_id: Box<UserId>,
},
}
pub(crate) async fn process(command: FederationCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
@@ -48,5 +55,8 @@ pub(crate) async fn process(command: FederationCommand, body: Vec<&str>) -> Resu
FederationCommand::FetchSupportWellKnown {
server_name,
} => fetch_support_well_known(body, server_name).await?,
FederationCommand::RemoteUserInRooms {
user_id,
} => remote_user_in_rooms(body, user_id).await?,
})
}
+305
View File
@@ -0,0 +1,305 @@
use std::sync::Arc;
use clap::Parser;
use regex::Regex;
use ruma::{
events::{
relation::InReplyTo,
room::message::{Relation::Reply, RoomMessageEventContent},
TimelineEventType,
},
OwnedRoomId, OwnedUserId, ServerName, UserId,
};
use serde_json::value::to_raw_value;
use tokio::sync::MutexGuard;
use tracing::error;
extern crate conduit_service as service;
use conduit::{Error, Result};
pub(crate) use service::admin::{AdminRoomEvent, Service};
use service::{admin::HandlerResult, pdu::PduBuilder};
use self::{fsck::FsckCommand, tester::TesterCommands};
use crate::{
appservice, appservice::AppserviceCommand, debug, debug::DebugCommand, escape_html, federation,
federation::FederationCommand, fsck, media, media::MediaCommand, query, query::QueryCommand, room,
room::RoomCommand, server, server::ServerCommand, services, tester, user, user::UserCommand,
};
pub(crate) const PAGE_SIZE: usize = 100;
#[cfg_attr(test, derive(Debug))]
#[derive(Parser)]
#[command(name = "@conduit:server.name:", version = env!("CARGO_PKG_VERSION"))]
pub(crate) enum AdminCommand {
#[command(subcommand)]
/// - Commands for managing appservices
Appservices(AppserviceCommand),
#[command(subcommand)]
/// - Commands for managing local users
Users(UserCommand),
#[command(subcommand)]
/// - Commands for managing rooms
Rooms(RoomCommand),
#[command(subcommand)]
/// - Commands for managing federation
Federation(FederationCommand),
#[command(subcommand)]
/// - Commands for managing the server
Server(ServerCommand),
#[command(subcommand)]
/// - Commands for managing media
Media(MediaCommand),
#[command(subcommand)]
/// - Commands for debugging things
Debug(DebugCommand),
#[command(subcommand)]
/// - Query all the database getters and iterators
Query(QueryCommand),
#[command(subcommand)]
/// - Query all the database getters and iterators
Fsck(FsckCommand),
#[command(subcommand)]
Tester(TesterCommands),
}
#[must_use]
pub fn handle(event: AdminRoomEvent, room: OwnedRoomId, user: OwnedUserId) -> HandlerResult {
Box::pin(handle_event(event, room, user))
}
async fn handle_event(event: AdminRoomEvent, admin_room: OwnedRoomId, server_user: OwnedUserId) -> Result<()> {
let (mut message_content, reply) = match event {
AdminRoomEvent::SendMessage(content) => (content, None),
AdminRoomEvent::ProcessMessage(room_message, reply_id) => {
(process_admin_message(room_message).await, Some(reply_id))
},
};
let mutex_state = Arc::clone(
services()
.globals
.roomid_mutex_state
.write()
.await
.entry(admin_room.clone())
.or_default(),
);
let state_lock = mutex_state.lock().await;
if let Some(reply) = reply {
message_content.relates_to = Some(Reply {
in_reply_to: InReplyTo {
event_id: reply.into(),
},
});
}
let response_pdu = PduBuilder {
event_type: TimelineEventType::RoomMessage,
content: to_raw_value(&message_content).expect("event is valid, we just created it"),
unsigned: None,
state_key: None,
redacts: None,
};
if let Err(e) = services()
.rooms
.timeline
.build_and_append_pdu(response_pdu, &server_user, &admin_room, &state_lock)
.await
{
handle_response_error(&e, &admin_room, &server_user, &state_lock).await?;
}
Ok(())
}
async fn handle_response_error(
e: &Error, admin_room: &OwnedRoomId, server_user: &UserId, state_lock: &MutexGuard<'_, ()>,
) -> Result<()> {
error!("Failed to build and append admin room response PDU: \"{e}\"");
let error_room_message = RoomMessageEventContent::text_plain(format!(
"Failed to build and append admin room PDU: \"{e}\"\n\nThe original admin command may have finished \
successfully, but we could not return the output."
));
let response_pdu = PduBuilder {
event_type: TimelineEventType::RoomMessage,
content: to_raw_value(&error_room_message).expect("event is valid, we just created it"),
unsigned: None,
state_key: None,
redacts: None,
};
services()
.rooms
.timeline
.build_and_append_pdu(response_pdu, server_user, admin_room, state_lock)
.await?;
Ok(())
}
// Parse and process a message from the admin room
async fn process_admin_message(room_message: String) -> RoomMessageEventContent {
let mut lines = room_message.lines().filter(|l| !l.trim().is_empty());
let command_line = lines.next().expect("each string has at least one line");
let body = lines.collect::<Vec<_>>();
let admin_command = match parse_admin_command(command_line) {
Ok(command) => command,
Err(error) => {
let server_name = services().globals.server_name();
let message = error.replace("server.name", server_name.as_str());
let html_message = usage_to_html(&message, server_name);
return RoomMessageEventContent::text_html(message, html_message);
},
};
match process_admin_command(admin_command, body).await {
Ok(reply_message) => reply_message,
Err(error) => {
let markdown_message = format!("Encountered an error while handling the command:\n```\n{error}\n```",);
let html_message = format!("Encountered an error while handling the command:\n<pre>\n{error}\n</pre>",);
RoomMessageEventContent::text_html(markdown_message, html_message)
},
}
}
// Parse chat messages from the admin room into an AdminCommand object
fn parse_admin_command(command_line: &str) -> Result<AdminCommand, String> {
// Note: argv[0] is `@conduit:servername:`, which is treated as the main command
let mut argv = command_line.split_whitespace().collect::<Vec<_>>();
// Replace `help command` with `command --help`
// Clap has a help subcommand, but it omits the long help description.
if argv.len() > 1 && argv[1] == "help" {
argv.remove(1);
argv.push("--help");
}
// Backwards compatibility with `register_appservice`-style commands
let command_with_dashes_argv1;
if argv.len() > 1 && argv[1].contains('_') {
command_with_dashes_argv1 = argv[1].replace('_', "-");
argv[1] = &command_with_dashes_argv1;
}
// Backwards compatibility with `register_appservice`-style commands
let command_with_dashes_argv2;
if argv.len() > 2 && argv[2].contains('_') {
command_with_dashes_argv2 = argv[2].replace('_', "-");
argv[2] = &command_with_dashes_argv2;
}
// if the user is using the `query` command (argv[1]), replace the database
// function/table calls with underscores to match the codebase
let command_with_dashes_argv3;
if argv.len() > 3 && argv[1].eq("query") {
command_with_dashes_argv3 = argv[3].replace('_', "-");
argv[3] = &command_with_dashes_argv3;
}
AdminCommand::try_parse_from(argv).map_err(|error| error.to_string())
}
async fn process_admin_command(command: AdminCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
let reply_message_content = match command {
AdminCommand::Appservices(command) => appservice::process(command, body).await?,
AdminCommand::Media(command) => media::process(command, body).await?,
AdminCommand::Users(command) => user::process(command, body).await?,
AdminCommand::Rooms(command) => room::process(command, body).await?,
AdminCommand::Federation(command) => federation::process(command, body).await?,
AdminCommand::Server(command) => server::process(command, body).await?,
AdminCommand::Debug(command) => debug::process(command, body).await?,
AdminCommand::Query(command) => query::process(command, body).await?,
AdminCommand::Fsck(command) => fsck::process(command, body).await?,
AdminCommand::Tester(command) => tester::process(command, body).await?,
};
Ok(reply_message_content)
}
// Utility to turn clap's `--help` text to HTML.
fn usage_to_html(text: &str, server_name: &ServerName) -> String {
// Replace `@conduit:servername:-subcmdname` with `@conduit:servername:
// subcmdname`
let text = text.replace(&format!("@conduit:{server_name}:-"), &format!("@conduit:{server_name}: "));
// For the conduit admin room, subcommands become main commands
let text = text.replace("SUBCOMMAND", "COMMAND");
let text = text.replace("subcommand", "command");
// Escape option names (e.g. `<element-id>`) since they look like HTML tags
let text = escape_html(&text);
// Italicize the first line (command name and version text)
let re = Regex::new("^(.*?)\n").expect("Regex compilation should not fail");
let text = re.replace_all(&text, "<em>$1</em>\n");
// Unmerge wrapped lines
let text = text.replace("\n ", " ");
// Wrap option names in backticks. The lines look like:
// -V, --version Prints version information
// And are converted to:
// <code>-V, --version</code>: Prints version information
// (?m) enables multi-line mode for ^ and $
let re = Regex::new("(?m)^ {4}(([a-zA-Z_&;-]+(, )?)+) +(.*)$").expect("Regex compilation should not fail");
let text = re.replace_all(&text, "<code>$1</code>: $4");
// Look for a `[commandbody]` tag. If it exists, use all lines below it that
// start with a `#` in the USAGE section.
let mut text_lines = text.lines().collect::<Vec<&str>>();
let mut command_body = String::new();
if let Some(line_index) = text_lines.iter().position(|line| *line == "[commandbody]") {
text_lines.remove(line_index);
while text_lines
.get(line_index)
.is_some_and(|line| line.starts_with('#'))
{
command_body += if text_lines[line_index].starts_with("# ") {
&text_lines[line_index][2..]
} else {
&text_lines[line_index][1..]
};
command_body += "[nobr]\n";
text_lines.remove(line_index);
}
}
let text = text_lines.join("\n");
// Improve the usage section
let text = if command_body.is_empty() {
// Wrap the usage line in code tags
let re = Regex::new("(?m)^USAGE:\n {4}(@conduit:.*)$").expect("Regex compilation should not fail");
re.replace_all(&text, "USAGE:\n<code>$1</code>").to_string()
} else {
// Wrap the usage line in a code block, and add a yaml block example
// This makes the usage of e.g. `register-appservice` more accurate
let re = Regex::new("(?m)^USAGE:\n {4}(.*?)\n\n").expect("Regex compilation should not fail");
re.replace_all(&text, "USAGE:\n<pre>$1[nobr]\n[commandbodyblock]</pre>")
.replace("[commandbodyblock]", &command_body)
};
// Add HTML line-breaks
text.replace("\n\n\n", "\n\n")
.replace('\n', "<br>\n")
.replace("[nobr]<br>", "")
}
@@ -1,7 +1,7 @@
use ruma::{events::room::message::RoomMessageEventContent, EventId};
use ruma::{events::room::message::RoomMessageEventContent, EventId, MxcUri};
use tracing::{debug, info};
use crate::{service::admin::MxcUri, services, Result};
use crate::{services, Result};
pub(crate) async fn delete(
_body: Vec<&str>, mxc: Option<Box<MxcUri>>, event_id: Option<Box<EventId>>,
@@ -139,14 +139,19 @@ pub(crate) async fn delete(
pub(crate) async fn delete_list(body: Vec<&str>) -> Result<RoomMessageEventContent> {
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" {
let mxc_list = body.clone().drain(1..body.len() - 1).collect::<Vec<_>>();
let mxc_list = body
.clone()
.drain(1..body.len().checked_sub(1).unwrap())
.collect::<Vec<_>>();
let mut mxc_deletion_count = 0;
let mut mxc_deletion_count: usize = 0;
for mxc in mxc_list {
debug!("Deleting MXC {mxc} in bulk");
services().media.delete(mxc.to_owned()).await?;
mxc_deletion_count += 1;
mxc_deletion_count = mxc_deletion_count
.checked_add(1)
.expect("mxc_deletion_count should not get this high");
}
return Ok(RoomMessageEventContent::text_plain(format!(
@@ -1,8 +1,8 @@
use clap::Subcommand;
use ruma::{events::room::message::RoomMessageEventContent, EventId};
use ruma::{events::room::message::RoomMessageEventContent, EventId, MxcUri};
use self::media_commands::{delete, delete_list, delete_past_remote_media};
use crate::{service::admin::MxcUri, Result};
use crate::Result;
pub(crate) mod media_commands;
+55
View File
@@ -0,0 +1,55 @@
pub(crate) mod appservice;
pub(crate) mod debug;
pub(crate) mod federation;
pub(crate) mod fsck;
pub(crate) mod handler;
pub(crate) mod media;
pub(crate) mod query;
pub(crate) mod room;
pub(crate) mod server;
pub(crate) mod tester;
pub(crate) mod user;
pub(crate) mod utils;
extern crate conduit_api as api;
extern crate conduit_core as conduit;
extern crate conduit_service as service;
pub(crate) use conduit::{mod_ctor, mod_dtor, Result};
pub use handler::handle;
pub(crate) use service::{services, user_is_local};
pub(crate) use crate::{
handler::Service,
utils::{escape_html, get_room_info},
};
mod_ctor! {}
mod_dtor! {}
#[cfg(test)]
mod test {
use clap::Parser;
use crate::handler::AdminCommand;
#[test]
fn get_help_short() { get_help_inner("-h"); }
#[test]
fn get_help_long() { get_help_inner("--help"); }
#[test]
fn get_help_subcommand() { get_help_inner("help"); }
fn get_help_inner(input: &str) {
let error = AdminCommand::try_parse_from(["argv[0] doesn't matter", input])
.unwrap_err()
.to_string();
// Search for a handful of keywords that suggest the help printed properly
assert!(error.contains("Usage:"));
assert!(error.contains("Commands:"));
assert!(error.contains("Options:"));
}
}
@@ -46,7 +46,7 @@ pub(crate) enum RoomAliasCommand {
room_alias_localpart: String,
},
/// - Remove an alias
/// - Remove a local alias
Remove {
/// The alias localpart to remove (`alias`, not `#alias:servername.tld`)
room_alias_localpart: String,
@@ -1,9 +1,9 @@
use std::fmt::Write as _;
use std::fmt::Write;
use ruma::{events::room::message::RoomMessageEventContent, RoomAliasId};
use super::RoomAliasCommand;
use crate::{service::admin::escape_html, services, Result};
use crate::{escape_html, services, Result};
pub(crate) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
match command {
@@ -79,12 +79,13 @@ pub(crate) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Resu
match aliases {
Ok(aliases) => {
let plain_list = aliases.iter().fold(String::new(), |mut output, alias| {
writeln!(output, "- {alias}").unwrap();
writeln!(output, "- {alias}").expect("should be able to write to string buffer");
output
});
let html_list = aliases.iter().fold(String::new(), |mut output, alias| {
writeln!(output, "<li>{}</li>", escape_html(alias.as_ref())).unwrap();
writeln!(output, "<li>{}</li>", escape_html(alias.as_ref()))
.expect("should be able to write to string buffer");
output
});
@@ -106,7 +107,8 @@ pub(crate) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Resu
let plain_list = aliases
.iter()
.fold(String::new(), |mut output, (alias, id)| {
writeln!(output, "- `{alias}` -> #{id}:{server_name}").unwrap();
writeln!(output, "- `{alias}` -> #{id}:{server_name}")
.expect("should be able to write to string buffer");
output
});
@@ -120,7 +122,7 @@ pub(crate) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Resu
escape_html(id.as_ref()),
server_name
)
.unwrap();
.expect("should be able to write to string buffer");
output
});
@@ -1,11 +1,8 @@
use std::fmt::Write as _;
use std::fmt::Write;
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId};
use crate::{
service::admin::{escape_html, get_room_info, PAGE_SIZE},
services, Result,
};
use crate::{escape_html, get_room_info, handler::PAGE_SIZE, services, Result};
pub(crate) async fn list(_body: Vec<&str>, page: Option<usize>) -> Result<RoomMessageEventContent> {
// TODO: i know there's a way to do this with clap, but i can't seem to find it
@@ -22,7 +19,7 @@ pub(crate) async fn list(_body: Vec<&str>, page: Option<usize>) -> Result<RoomMe
let rooms = rooms
.into_iter()
.skip(page.saturating_sub(1) * PAGE_SIZE)
.skip(page.saturating_sub(1).saturating_mul(PAGE_SIZE))
.take(PAGE_SIZE)
.collect::<Vec<_>>();
@@ -51,7 +48,7 @@ pub(crate) async fn list(_body: Vec<&str>, page: Option<usize>) -> Result<RoomMe
members,
escape_html(name)
)
.unwrap();
.expect("should be able to write to string buffer");
output
})
);
@@ -1,12 +1,9 @@
use std::fmt::Write as _;
use std::fmt::Write;
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId};
use super::RoomDirectoryCommand;
use crate::{
service::admin::{escape_html, get_room_info, PAGE_SIZE},
services, Result,
};
use crate::{escape_html, get_room_info, handler::PAGE_SIZE, services, Result};
pub(crate) async fn process(command: RoomDirectoryCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
match command {
@@ -39,7 +36,7 @@ pub(crate) async fn process(command: RoomDirectoryCommand, _body: Vec<&str>) ->
let rooms = rooms
.into_iter()
.skip(page.saturating_sub(1) * PAGE_SIZE)
.skip(page.saturating_sub(1).saturating_mul(PAGE_SIZE))
.take(PAGE_SIZE)
.collect::<Vec<_>>();
@@ -68,7 +65,7 @@ pub(crate) async fn process(command: RoomDirectoryCommand, _body: Vec<&str>) ->
members,
escape_html(name.as_ref())
)
.unwrap();
.expect("should be able to write to string buffer");
output
})
);
@@ -1,16 +1,16 @@
use std::fmt::Write as _;
use std::fmt::Write;
use api::client_server::{get_alias_helper, leave_room};
use ruma::{
events::room::message::RoomMessageEventContent, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, RoomOrAliasId,
};
use tracing::{debug, error, info, warn};
use super::RoomModerationCommand;
use crate::{
api::client_server::{get_alias_helper, leave_room},
service::admin::{escape_html, Service},
services, Result,
use super::{
super::{escape_html, Service},
RoomModerationCommand,
};
use crate::{services, user_is_local, Result};
pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
match command {
@@ -25,7 +25,7 @@ pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) ->
.try_into()
.expect("#admins:server_name is a valid alias name");
if let Some(admin_room_id) = Service::get_admin_room()? {
if let Some(admin_room_id) = Service::get_admin_room().await? {
if room.to_string().eq(&admin_room_id) || room.to_string().eq(&admin_room_alias) {
return Ok(RoomMessageEventContent::text_plain("Not allowed to ban the admin room."));
}
@@ -102,18 +102,17 @@ pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) ->
.room_members(&room_id)
.filter_map(|user| {
user.ok().filter(|local_user| {
local_user.server_name() == services().globals.server_name()
// additional wrapped check here is to avoid adding remote users
// who are in the admin room to the list of local users (would fail auth check)
&& (local_user.server_name()
== services().globals.server_name()
&& services()
.users
.is_admin(local_user)
.unwrap_or(true)) // since this is a force
// operation, assume user
// is an admin if somehow
// this fails
user_is_local(local_user)
// additional wrapped check here is to avoid adding remote users
// who are in the admin room to the list of local users (would fail auth check)
&& (user_is_local(local_user)
&& services()
.users
.is_admin(local_user)
.unwrap_or(true)) // since this is a force
// operation, assume user
// is an admin if somehow
// this fails
})
})
.collect::<Vec<OwnedUserId>>()
@@ -123,7 +122,9 @@ pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) ->
&local_user, &room_id
);
_ = leave_room(&local_user, &room_id, None).await;
if let Err(e) = leave_room(&local_user, &room_id, None).await {
warn!(%e, "Failed to leave room");
}
}
} else {
for local_user in services()
@@ -133,14 +134,14 @@ pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) ->
.filter_map(|user| {
user.ok().filter(|local_user| {
local_user.server_name() == services().globals.server_name()
// additional wrapped check here is to avoid adding remote users
// who are in the admin room to the list of local users (would fail auth check)
&& (local_user.server_name()
== services().globals.server_name()
&& !services()
.users
.is_admin(local_user)
.unwrap_or(false))
// additional wrapped check here is to avoid adding remote users
// who are in the admin room to the list of local users (would fail auth check)
&& (local_user.server_name()
== services().globals.server_name()
&& !services()
.users
.is_admin(local_user)
.unwrap_or(false))
})
})
.collect::<Vec<OwnedUserId>>()
@@ -169,8 +170,8 @@ pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) ->
}
Ok(RoomMessageEventContent::text_plain(
"Room banned and removed all our local users, use disable-room to stop receiving new inbound \
federation events as well if needed.",
"Room banned and removed all our local users, use `!admin federation disable-room` to stop receiving \
new inbound federation events as well if needed.",
))
},
RoomModerationCommand::BanListOfRooms {
@@ -190,7 +191,7 @@ pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) ->
for &room in &rooms_s {
match <&RoomOrAliasId>::try_from(room) {
Ok(room_alias_or_id) => {
if let Some(admin_room_id) = Service::get_admin_room()? {
if let Some(admin_room_id) = Service::get_admin_room().await? {
if room.to_owned().eq(&admin_room_id) || room.to_owned().eq(&admin_room_alias) {
info!("User specified admin room in bulk ban list, ignoring");
continue;
@@ -308,19 +309,19 @@ pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) ->
.filter_map(|user| {
user.ok().filter(|local_user| {
local_user.server_name() == services().globals.server_name()
// additional wrapped check here is to avoid adding remote users
// who are in the admin room to the list of local users (would fail auth check)
&& (local_user.server_name()
== services().globals.server_name()
&& services()
.users
.is_admin(local_user)
.unwrap_or(true)) // since this is a
// force operation,
// assume user is
// an admin if
// somehow this
// fails
// additional wrapped check here is to avoid adding remote users
// who are in the admin room to the list of local users (would fail auth check)
&& (local_user.server_name()
== services().globals.server_name()
&& services()
.users
.is_admin(local_user)
.unwrap_or(true)) // since this is a
// force operation,
// assume user is
// an admin if
// somehow this
// fails
})
})
.collect::<Vec<OwnedUserId>>()
@@ -330,7 +331,9 @@ pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) ->
admins too)",
&local_user, room_id
);
_ = leave_room(&local_user, &room_id, None).await;
if let Err(e) = leave_room(&local_user, &room_id, None).await {
warn!(%e, "Failed to leave room");
}
}
} else {
for local_user in services()
@@ -340,14 +343,14 @@ pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) ->
.filter_map(|user| {
user.ok().filter(|local_user| {
local_user.server_name() == services().globals.server_name()
// additional wrapped check here is to avoid adding remote users
// who are in the admin room to the list of local users (would fail auth check)
&& (local_user.server_name()
== services().globals.server_name()
&& !services()
.users
.is_admin(local_user)
.unwrap_or(false))
// additional wrapped check here is to avoid adding remote users
// who are in the admin room to the list of local users (would fail auth check)
&& (local_user.server_name()
== services().globals.server_name()
&& !services()
.users
.is_admin(local_user)
.unwrap_or(false))
})
})
.collect::<Vec<OwnedUserId>>()
@@ -5,13 +5,16 @@ use ruma::events::room::message::RoomMessageEventContent;
use self::server_commands::{
backup_database, clear_database_caches, clear_service_caches, list_backups, list_database_files, memory_usage,
show_config,
show_config, uptime,
};
use crate::Result;
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
pub(crate) enum ServerCommand {
/// - Time elapsed since startup
Uptime,
/// - Show configuration values
ShowConfig,
@@ -43,6 +46,7 @@ pub(crate) enum ServerCommand {
pub(crate) async fn process(command: ServerCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
Ok(match command {
ServerCommand::Uptime => uptime(body).await?,
ServerCommand::ShowConfig => show_config(body).await?,
ServerCommand::MemoryUsage => memory_usage(body).await?,
ServerCommand::ClearDatabaseCaches {
@@ -2,17 +2,41 @@ use ruma::events::room::message::RoomMessageEventContent;
use crate::{services, Result};
pub(crate) async fn uptime(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
let seconds = services()
.server
.started
.elapsed()
.expect("standard duration")
.as_secs();
let result = format!(
"up {} days, {} hours, {} minutes, {} seconds.",
seconds / 86400,
(seconds % 86400) / 60 / 60,
(seconds % 3600) / 60,
seconds % 60,
);
Ok(RoomMessageEventContent::notice_html(String::new(), result))
}
pub(crate) async fn show_config(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
// Construct and send the response
Ok(RoomMessageEventContent::text_plain(format!("{}", services().globals.config)))
}
pub(crate) async fn memory_usage(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
let response1 = services().memory_usage().await;
let response2 = services().globals.db.memory_usage();
let response0 = services().memory_usage().await;
let response1 = services().globals.db.memory_usage();
let response2 = conduit::alloc::memory_usage();
Ok(RoomMessageEventContent::text_plain(format!(
"Services:\n{response1}\n\nDatabase:\n{response2}"
"Services:\n{response0}\n\nDatabase:\n{response1}\n{}",
if !response2.is_empty() {
format!("Allocator:\n {response2}")
} else {
String::new()
}
)))
}
@@ -45,12 +69,15 @@ pub(crate) async fn backup_database(_body: Vec<&str>) -> Result<RoomMessageEvent
));
}
let mut result = tokio::task::spawn_blocking(move || match services().globals.db.backup() {
Ok(()) => String::new(),
Err(e) => (*e).to_string(),
})
.await
.unwrap();
let mut result = services()
.server
.runtime()
.spawn_blocking(move || match services().globals.db.backup() {
Ok(()) => String::new(),
Err(e) => (*e).to_string(),
})
.await
.unwrap();
if result.is_empty() {
result = services().globals.db.backup_list()?;
+14
View File
@@ -0,0 +1,14 @@
use ruma::events::room::message::RoomMessageEventContent;
use crate::Result;
#[cfg_attr(test, derive(Debug))]
#[derive(clap::Subcommand)]
pub(crate) enum TesterCommands {
Tester,
}
pub(crate) async fn process(command: TesterCommands, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
Ok(match command {
TesterCommands::Tester => RoomMessageEventContent::notice_plain(String::from("completed")),
})
}
@@ -1,14 +1,13 @@
use std::{fmt::Write as _, sync::Arc};
use itertools::Itertools;
use api::client_server::{join_room_by_id_helper, leave_all_rooms};
use conduit::utils;
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, UserId};
use tracing::{error, info, warn};
use crate::{
api::client_server::{join_room_by_id_helper, leave_all_rooms, AUTO_GEN_PASSWORD_LENGTH},
service::admin::{escape_html, get_room_info},
services, utils, Result,
};
use crate::{escape_html, get_room_info, services, user_is_local, Result};
const AUTO_GEN_PASSWORD_LENGTH: usize = 25;
pub(crate) async fn list(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
match services().users.list_local_users() {
@@ -37,6 +36,12 @@ pub(crate) async fn create(
},
};
if !user_is_local(&user_id) {
return Ok(RoomMessageEventContent::text_plain(format!(
"User {user_id} does not belong to our server."
)));
}
if user_id.is_historical() {
return Ok(RoomMessageEventContent::text_plain(format!(
"Userid {user_id} is not allowed due to historical"
@@ -54,8 +59,14 @@ pub(crate) async fn create(
// If `new_user_displayname_suffix` is set, registration will push whatever
// content is set to the user's display name with a space before it
if !services().globals.new_user_displayname_suffix().is_empty() {
displayname.push_str(&(" ".to_owned() + services().globals.new_user_displayname_suffix()));
if !services()
.globals
.config
.new_user_displayname_suffix
.is_empty()
{
write!(displayname, " {}", services().globals.config.new_user_displayname_suffix)
.expect("should be able to write to string buffer");
}
services()
@@ -99,7 +110,7 @@ pub(crate) async fn create(
)
.await
{
Ok(_) => {
Ok(_response) => {
info!("Automatically joined room {room} for user {user_id}");
},
Err(e) => {
@@ -155,7 +166,7 @@ pub(crate) async fn deactivate(
services().users.deactivate_account(&user_id)?;
if leave_rooms {
leave_all_rooms(&user_id).await?;
leave_all_rooms(&user_id).await;
}
Ok(RoomMessageEventContent::text_plain(format!(
@@ -227,7 +238,7 @@ pub(crate) async fn deactivate_all(body: Vec<&str>, leave_rooms: bool, force: bo
}
}
let mut deactivation_count = 0;
let mut deactivation_count: u16 = 0;
let mut admins = Vec::new();
if !force {
@@ -264,13 +275,13 @@ pub(crate) async fn deactivate_all(body: Vec<&str>, leave_rooms: bool, force: bo
}
if services().users.deactivate_account(user_id).is_ok() {
deactivation_count += 1;
deactivation_count = deactivation_count.saturating_add(1);
}
}
if leave_rooms {
for &user_id in &user_ids {
_ = leave_all_rooms(user_id).await;
leave_all_rooms(user_id).await;
}
}
@@ -304,7 +315,7 @@ pub(crate) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Resu
},
};
if user_id.server_name() != services().globals.server_name() {
if !user_is_local(&user_id) {
return Ok(RoomMessageEventContent::text_plain("User does not belong to our server."));
}
@@ -318,8 +329,6 @@ pub(crate) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Resu
.rooms_joined(&user_id)
.filter_map(Result::ok)
.map(|room_id| get_room_info(&room_id))
.sorted_unstable()
.dedup()
.collect();
if rooms.is_empty() {
@@ -330,7 +339,8 @@ pub(crate) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Resu
rooms.reverse();
let output_plain = format!(
"Rooms {user_id} Joined:\n{}",
"Rooms {user_id} Joined ({}):\n{}",
rooms.len(),
rooms
.iter()
.map(|(id, members, name)| format!("{id}\tMembers: {members}\tName: {name}"))
@@ -338,8 +348,9 @@ pub(crate) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Resu
.join("\n")
);
let output_html = format!(
"<table><caption>Rooms {user_id} \
Joined</caption>\n<tr><th>id</th>\t<th>members</th>\t<th>name</th></tr>\n{}</table>",
"<table><caption>Rooms {user_id} Joined \
({})</caption>\n<tr><th>id</th>\t<th>members</th>\t<th>name</th></tr>\n{}</table>",
rooms.len(),
rooms
.iter()
.fold(String::new(), |mut output, (id, members, name)| {
+30
View File
@@ -0,0 +1,30 @@
pub(crate) use conduit::utils::HtmlEscape;
use ruma::OwnedRoomId;
use crate::services;
pub(crate) fn escape_html(s: &str) -> String {
s.replace('&', "&amp;")
.replace('<', "&lt;")
.replace('>', "&gt;")
}
pub(crate) fn get_room_info(id: &OwnedRoomId) -> (OwnedRoomId, u64, String) {
(
id.clone(),
services()
.rooms
.state_cache
.room_joined_count(id)
.ok()
.flatten()
.unwrap_or(0),
services()
.rooms
.state_accessor
.get_name(id)
.ok()
.flatten()
.unwrap_or_else(|| id.to_string()),
)
}
+66
View File
@@ -0,0 +1,66 @@
[package]
name = "conduit_api"
version.workspace = true
edition.workspace = true
[lib]
path = "mod.rs"
crate-type = [
"rlib",
# "dylib",
]
[features]
default = [
"element_hacks",
"gzip_compression",
"brotli_compression",
"release_max_log_level",
]
element_hacks = []
dev_release_log_level = []
release_max_log_level = [
"tracing/max_level_trace",
"tracing/release_max_level_info",
"log/max_level_trace",
"log/release_max_level_info",
]
gzip_compression = [
"reqwest/gzip",
]
brotli_compression = [
"reqwest/brotli",
]
[dependencies]
argon2.workspace = true
axum-extra.workspace = true
axum.workspace = true
base64.workspace = true
bytes.workspace = true
conduit-core.workspace = true
conduit-database.workspace = true
conduit-service.workspace = true
futures-util.workspace = true
hmac.workspace = true
http.workspace = true
hyper.workspace = true
image.workspace = true
ipaddress.workspace = true
jsonwebtoken.workspace = true
log.workspace = true
rand.workspace = true
reqwest.workspace = true
ruma.workspace = true
serde_html_form.workspace = true
serde_json.workspace = true
serde.workspace = true
sha-1.workspace = true
thiserror.workspace = true
tokio.workspace = true
tracing.workspace = true
webpage.workspace = true
[lints]
workspace = true
+52 -17
View File
@@ -1,8 +1,11 @@
use std::fmt::Write;
use conduit::debug_info;
use register::RegistrationKind;
use ruma::{
api::client::{
account::{
change_password, deactivate, get_3pids, get_username_availability,
change_password, check_registration_token_validity, deactivate, get_3pids, get_username_availability,
register::{self, LoginType},
request_3pid_management_token_via_email, request_3pid_management_token_via_msisdn, whoami,
ThirdPartyIdRemovalStatus,
@@ -17,8 +20,11 @@ use tracing::{error, info, warn};
use super::{DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH};
use crate::{
api::client_server::{self, join_room_by_id_helper},
service, services, utils, Error, Result, Ruma,
client_server::{self, join_room_by_id_helper},
service::user_is_local,
services,
utils::{self},
Error, Result, Ruma,
};
const RANDOM_USER_ID_LENGTH: usize = 10;
@@ -40,7 +46,7 @@ pub(crate) async fn get_register_available_route(
// Validate user id
let user_id = UserId::parse_with_server_name(body.username.to_lowercase(), services().globals.server_name())
.ok()
.filter(|user_id| !user_id.is_historical() && user_id.server_name() == services().globals.server_name())
.filter(|user_id| !user_id.is_historical() && user_is_local(user_id))
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
// Check if username is creative enough
@@ -125,9 +131,7 @@ pub(crate) async fn register_route(body: Ruma<register::v3::Request>) -> Result<
let proposed_user_id =
UserId::parse_with_server_name(username.to_lowercase(), services().globals.server_name())
.ok()
.filter(|user_id| {
!user_id.is_historical() && user_id.server_name() == services().globals.server_name()
})
.filter(|user_id| !user_id.is_historical() && user_is_local(user_id))
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
if services().users.exists(&proposed_user_id)? {
@@ -238,7 +242,8 @@ pub(crate) async fn register_route(body: Ruma<register::v3::Request>) -> Result<
// If `new_user_displayname_suffix` is set, registration will push whatever
// content is set to the user's display name with a space before it
if !services().globals.new_user_displayname_suffix().is_empty() {
displayname.push_str(&(" ".to_owned() + services().globals.new_user_displayname_suffix()));
write!(displayname, " {}", services().globals.config.new_user_displayname_suffix)
.expect("should be able to write to string buffer");
}
services()
@@ -286,19 +291,23 @@ pub(crate) async fn register_route(body: Ruma<register::v3::Request>) -> Result<
.users
.create_device(&user_id, &device_id, &token, body.initial_device_display_name.clone())?;
info!("New user \"{}\" registered on this server.", user_id);
debug_info!(%user_id, %device_id, "User account was created");
// log in conduit admin channel if a non-guest user registered
if body.appservice_info.is_none() && !is_guest {
info!("New user \"{user_id}\" registered on this server.");
services()
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"New user \"{user_id}\" registered on this server."
)));
)))
.await;
}
// log in conduit admin channel if a guest registered
if body.appservice_info.is_none() && is_guest && services().globals.log_guest_registrations() {
info!("New guest user \"{user_id}\" registered on this server.");
if let Some(device_display_name) = &body.initial_device_display_name {
if body
.initial_device_display_name
@@ -310,27 +319,30 @@ pub(crate) async fn register_route(body: Ruma<register::v3::Request>) -> Result<
.send_message(RoomMessageEventContent::notice_plain(format!(
"Guest user \"{user_id}\" with device display name `{device_display_name}` registered on this \
server."
)));
)))
.await;
} else {
services()
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"Guest user \"{user_id}\" with no device display name registered on this server.",
)));
)))
.await;
}
} else {
services()
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"Guest user \"{user_id}\" with no device display name registered on this server.",
)));
)))
.await;
}
}
// If this is the first real user, grant them admin privileges except for guest
// users Note: the server user, @conduit:servername, is generated first
if !is_guest {
if let Some(admin_room) = service::admin::Service::get_admin_room()? {
if let Some(admin_room) = service::admin::Service::get_admin_room().await? {
if services()
.rooms
.state_cache
@@ -461,7 +473,8 @@ pub(crate) async fn change_password_route(
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"User {sender_user} changed their password."
)));
)))
.await;
Ok(change_password::v3::Response {})
}
@@ -526,7 +539,7 @@ pub(crate) async fn deactivate_route(body: Ruma<deactivate::v3::Request>) -> Res
}
// Make the user leave all rooms before deactivation
client_server::leave_all_rooms(sender_user).await?;
client_server::leave_all_rooms(sender_user).await;
// Remove devices and mark account as deactivated
services().users.deactivate_account(sender_user)?;
@@ -536,7 +549,8 @@ pub(crate) async fn deactivate_route(body: Ruma<deactivate::v3::Request>) -> Res
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"User {sender_user} deactivated their account."
)));
)))
.await;
Ok(deactivate::v3::Response {
id_server_unbind_result: ThirdPartyIdRemovalStatus::NoSupport,
@@ -585,3 +599,24 @@ pub(crate) async fn request_3pid_management_token_via_msisdn_route(
"Third party identifier is not allowed",
))
}
/// # `GET /_matrix/client/v1/register/m.login.registration_token/validity`
///
/// Checks if the provided registration token is valid at the time of checking
///
/// Currently does not have any ratelimiting, and this isn't very practical as
/// there is only one registration token allowed.
pub(crate) async fn check_registration_token_validity(
body: Ruma<check_registration_token_validity::v1::Request>,
) -> Result<check_registration_token_validity::v1::Response> {
let Some(reg_token) = services().globals.config.registration_token.clone() else {
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"Server does not allow token registration.",
));
};
Ok(check_registration_token_validity::v1::Response {
valid: reg_token == body.token,
})
}
+64 -74
View File
@@ -8,38 +8,30 @@ use ruma::{
},
federation,
},
OwnedRoomAliasId, OwnedServerName,
OwnedRoomAliasId, OwnedRoomId, OwnedServerName,
};
use tracing::debug;
use crate::{debug_info, debug_warn, services, Error, Result, Ruma};
use crate::{
debug_info, debug_warn,
service::{appservice::RegistrationInfo, server_is_ours},
services, Error, Result, Ruma,
};
/// # `PUT /_matrix/client/v3/directory/room/{roomAlias}`
///
/// Creates a new room alias on this server.
pub(crate) async fn create_alias_route(body: Ruma<create_alias::v3::Request>) -> Result<create_alias::v3::Response> {
if body.room_alias.server_name() != services().globals.server_name() {
return Err(Error::BadRequest(ErrorKind::InvalidParam, "Alias is from another server."));
}
alias_checks(&body.room_alias, &body.appservice_info).await?;
// this isn't apart of alias_checks or delete alias route because we should
// allow removing forbidden room aliases
if services()
.globals
.forbidden_alias_names()
.is_match(body.room_alias.alias())
{
return Err(Error::BadRequest(ErrorKind::Unknown, "Room alias is forbidden."));
}
if let Some(ref info) = body.appservice_info {
if !info.aliases.is_match(body.room_alias.as_str()) {
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias is not in namespace."));
}
} else if services()
.appservice
.is_exclusive_alias(&body.room_alias)
.await
{
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias reserved by appservice."));
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room alias is forbidden."));
}
if services()
@@ -73,10 +65,7 @@ pub(crate) async fn create_alias_route(body: Ruma<create_alias::v3::Request>) ->
/// - TODO: additional access control checks
/// - TODO: Update canonical alias event
pub(crate) async fn delete_alias_route(body: Ruma<delete_alias::v3::Request>) -> Result<delete_alias::v3::Response> {
if body.room_alias.server_name() != services().globals.server_name() {
return Err(Error::BadRequest(ErrorKind::InvalidParam, "Alias is from another server."));
}
alias_checks(&body.room_alias, &body.appservice_info).await?;
if services()
.rooms
.alias
@@ -86,18 +75,6 @@ pub(crate) async fn delete_alias_route(body: Ruma<delete_alias::v3::Request>) ->
return Err(Error::BadRequest(ErrorKind::NotFound, "Alias does not exist."));
}
if let Some(ref info) = body.appservice_info {
if !info.aliases.is_match(body.room_alias.as_str()) {
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias is not in namespace."));
}
} else if services()
.appservice
.is_exclusive_alias(&body.room_alias)
.await
{
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias reserved by appservice."));
}
if services()
.rooms
.alias
@@ -122,11 +99,11 @@ pub(crate) async fn get_alias_route(body: Ruma<get_alias::v3::Request>) -> Resul
get_alias_helper(body.body.room_alias, None).await
}
pub(crate) async fn get_alias_helper(
pub async fn get_alias_helper(
room_alias: OwnedRoomAliasId, servers: Option<Vec<OwnedServerName>>,
) -> Result<get_alias::v3::Response> {
debug!("get_alias_helper servers: {servers:?}");
if room_alias.server_name() != services().globals.server_name()
if !server_is_ours(room_alias.server_name())
&& (!servers
.as_ref()
.is_some_and(|servers| servers.contains(&services().globals.server_name().to_owned()))
@@ -180,47 +157,21 @@ pub(crate) async fn get_alias_helper(
if let Ok(response) = response {
let room_id = response.room_id;
let mut servers = response.servers;
let mut pre_servers = response.servers;
// since the room alis server responded, insert it into the list
pre_servers.push(room_alias.server_name().into());
// since the room alias server_name responded, insert it into the list
servers.push(room_alias.server_name().into());
// find active servers in room state cache to suggest
servers.extend(
services()
.rooms
.state_cache
.room_servers(&room_id)
.filter_map(Result::ok),
let servers = room_available_servers(&room_id, &room_alias, &Some(pre_servers));
debug_warn!(
"room alias servers from federation response for room ID {room_id} and room alias {room_alias}: \
{servers:?}"
);
servers.sort_unstable();
servers.dedup();
// shuffle list of servers randomly after sort and dedupe
servers.shuffle(&mut rand::thread_rng());
// prefer the very first server to be ourselves if available, else prefer the
// room alias server first
if let Some(server_index) = servers
.iter()
.position(|server| server == services().globals.server_name())
{
servers.remove(server_index);
servers.insert(0, services().globals.server_name().to_owned());
} else if let Some(alias_server_index) = servers
.iter()
.position(|server| server == room_alias.server_name())
{
servers.remove(alias_server_index);
servers.insert(0, room_alias.server_name().into());
}
return Ok(get_alias::v3::Response::new(room_id, servers));
}
return Err(Error::BadRequest(
ErrorKind::Unknown,
ErrorKind::NotFound,
"No servers could assist in resolving the room alias",
));
}
@@ -260,28 +211,67 @@ pub(crate) async fn get_alias_helper(
return Err(Error::BadRequest(ErrorKind::NotFound, "Room with alias not found."));
};
let servers = room_available_servers(&room_id, &room_alias, &None);
debug_warn!("room alias servers for room ID {room_id} and room alias {room_alias}");
Ok(get_alias::v3::Response::new(room_id, servers))
}
fn room_available_servers(
room_id: &OwnedRoomId, room_alias: &OwnedRoomAliasId, pre_servers: &Option<Vec<OwnedServerName>>,
) -> Vec<OwnedServerName> {
// find active servers in room state cache to suggest
let mut servers: Vec<OwnedServerName> = services()
.rooms
.state_cache
.room_servers(&room_id)
.room_servers(room_id)
.filter_map(Result::ok)
.collect();
// push any servers we want in the list already (e.g. responded remote alias
// servers, room alias server itself)
if let Some(pre_servers) = pre_servers {
servers.extend(pre_servers.clone());
};
servers.sort_unstable();
servers.dedup();
// shuffle list of servers randomly after sort and dedupe
servers.shuffle(&mut rand::thread_rng());
// insert our server as the very first choice if in list
// insert our server as the very first choice if in list, else check if we can
// prefer the room alias server first
if let Some(server_index) = servers
.iter()
.position(|server| server == services().globals.server_name())
.position(|server_name| server_is_ours(server_name))
{
servers.remove(server_index);
servers.insert(0, services().globals.server_name().to_owned());
} else if let Some(alias_server_index) = servers
.iter()
.position(|server| server == room_alias.server_name())
{
servers.remove(alias_server_index);
servers.insert(0, room_alias.server_name().into());
}
Ok(get_alias::v3::Response::new(room_id, servers))
servers
}
async fn alias_checks(room_alias: &OwnedRoomAliasId, appservice_info: &Option<RegistrationInfo>) -> Result<()> {
if !server_is_ours(room_alias.server_name()) {
return Err(Error::BadRequest(ErrorKind::InvalidParam, "Alias is from another server."));
}
if let Some(ref info) = appservice_info {
if !info.aliases.is_match(room_alias.as_str()) {
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias is not in namespace."));
}
} else if services().appservice.is_exclusive_alias(room_alias).await {
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias reserved by appservice."));
}
Ok(())
}
+58 -43
View File
@@ -1,11 +1,14 @@
use ruma::api::client::{
backup::{
add_backup_keys, add_backup_keys_for_room, add_backup_keys_for_session, create_backup_version,
delete_backup_keys, delete_backup_keys_for_room, delete_backup_keys_for_session, delete_backup_version,
get_backup_info, get_backup_keys, get_backup_keys_for_room, get_backup_keys_for_session,
get_latest_backup_info, update_backup_version,
use ruma::{
api::client::{
backup::{
add_backup_keys, add_backup_keys_for_room, add_backup_keys_for_session, create_backup_version,
delete_backup_keys, delete_backup_keys_for_room, delete_backup_keys_for_session, delete_backup_version,
get_backup_info, get_backup_keys, get_backup_keys_for_room, get_backup_keys_for_session,
get_latest_backup_info, update_backup_version,
},
error::ErrorKind,
},
error::ErrorKind,
UInt,
};
use crate::{services, Error, Result, Ruma};
@@ -52,17 +55,18 @@ pub(crate) async fn get_latest_backup_info_route(
let (version, algorithm) = services()
.key_backups
.get_latest_backup(sender_user)?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Key backup does not exist."))?;
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Key backup does not exist."))?;
Ok(get_latest_backup_info::v3::Response {
algorithm,
count: (services().key_backups.count_keys(sender_user, &version)? as u32).into(),
count: (UInt::try_from(services().key_backups.count_keys(sender_user, &version)?)
.expect("user backup keys count should not be that high")),
etag: services().key_backups.get_etag(sender_user, &version)?,
version,
})
}
/// # `GET /_matrix/client/r0/room_keys/version`
/// # `GET /_matrix/client/v3/room_keys/version/{version}`
///
/// Get information about an existing backup.
pub(crate) async fn get_backup_info_route(
@@ -72,14 +76,16 @@ pub(crate) async fn get_backup_info_route(
let algorithm = services()
.key_backups
.get_backup(sender_user, &body.version)?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Key backup does not exist."))?;
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Key backup does not exist."))?;
Ok(get_backup_info::v3::Response {
algorithm,
count: (services()
.key_backups
.count_keys(sender_user, &body.version)? as u32)
.into(),
count: (UInt::try_from(
services()
.key_backups
.count_keys(sender_user, &body.version)?,
)
.expect("user backup keys count should not be that high")),
etag: services()
.key_backups
.get_etag(sender_user, &body.version)?,
@@ -139,10 +145,12 @@ pub(crate) async fn add_backup_keys_route(
}
Ok(add_backup_keys::v3::Response {
count: (services()
.key_backups
.count_keys(sender_user, &body.version)? as u32)
.into(),
count: (UInt::try_from(
services()
.key_backups
.count_keys(sender_user, &body.version)?,
)
.expect("user backup keys count should not be that high")),
etag: services()
.key_backups
.get_etag(sender_user, &body.version)?,
@@ -181,10 +189,12 @@ pub(crate) async fn add_backup_keys_for_room_route(
}
Ok(add_backup_keys_for_room::v3::Response {
count: (services()
.key_backups
.count_keys(sender_user, &body.version)? as u32)
.into(),
count: (UInt::try_from(
services()
.key_backups
.count_keys(sender_user, &body.version)?,
)
.expect("user backup keys count should not be that high")),
etag: services()
.key_backups
.get_etag(sender_user, &body.version)?,
@@ -221,10 +231,12 @@ pub(crate) async fn add_backup_keys_for_session_route(
.add_key(sender_user, &body.version, &body.room_id, &body.session_id, &body.session_data)?;
Ok(add_backup_keys_for_session::v3::Response {
count: (services()
.key_backups
.count_keys(sender_user, &body.version)? as u32)
.into(),
count: (UInt::try_from(
services()
.key_backups
.count_keys(sender_user, &body.version)?,
)
.expect("user backup keys count should not be that high")),
etag: services()
.key_backups
.get_etag(sender_user, &body.version)?,
@@ -274,10 +286,7 @@ pub(crate) async fn get_backup_keys_for_session_route(
let key_data = services()
.key_backups
.get_session(sender_user, &body.version, &body.room_id, &body.session_id)?
.ok_or(Error::BadRequest(
ErrorKind::NotFound,
"Backup key not found for this user's session.",
))?;
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Backup key not found for this user's session."))?;
Ok(get_backup_keys_for_session::v3::Response {
key_data,
@@ -297,10 +306,12 @@ pub(crate) async fn delete_backup_keys_route(
.delete_all_keys(sender_user, &body.version)?;
Ok(delete_backup_keys::v3::Response {
count: (services()
.key_backups
.count_keys(sender_user, &body.version)? as u32)
.into(),
count: (UInt::try_from(
services()
.key_backups
.count_keys(sender_user, &body.version)?,
)
.expect("user backup keys count should not be that high")),
etag: services()
.key_backups
.get_etag(sender_user, &body.version)?,
@@ -320,10 +331,12 @@ pub(crate) async fn delete_backup_keys_for_room_route(
.delete_room_keys(sender_user, &body.version, &body.room_id)?;
Ok(delete_backup_keys_for_room::v3::Response {
count: (services()
.key_backups
.count_keys(sender_user, &body.version)? as u32)
.into(),
count: (UInt::try_from(
services()
.key_backups
.count_keys(sender_user, &body.version)?,
)
.expect("user backup keys count should not be that high")),
etag: services()
.key_backups
.get_etag(sender_user, &body.version)?,
@@ -343,10 +356,12 @@ pub(crate) async fn delete_backup_keys_for_session_route(
.delete_room_key(sender_user, &body.version, &body.room_id, &body.session_id)?;
Ok(delete_backup_keys_for_session::v3::Response {
count: (services()
.key_backups
.count_keys(sender_user, &body.version)? as u32)
.into(),
count: (UInt::try_from(
services()
.key_backups
.count_keys(sender_user, &body.version)?,
)
.expect("user backup keys count should not be that high")),
etag: services()
.key_backups
.get_etag(sender_user, &body.version)?,
+2 -15
View File
@@ -1,8 +1,7 @@
use std::collections::BTreeMap;
use ruma::api::client::discovery::get_capabilities::{
self, Capabilities, ChangePasswordCapability, RoomVersionStability, RoomVersionsCapability, SetAvatarUrlCapability,
SetDisplayNameCapability, ThirdPartyIdChangesCapability,
self, Capabilities, RoomVersionStability, RoomVersionsCapability, ThirdPartyIdChangesCapability,
};
use crate::{services, Result, Ruma};
@@ -22,24 +21,12 @@ pub(crate) async fn get_capabilities_route(
available.insert(room_version.clone(), RoomVersionStability::Stable);
}
let mut capabilities = Capabilities::new();
let mut capabilities = Capabilities::default();
capabilities.room_versions = RoomVersionsCapability {
default: services().globals.default_room_version(),
available,
};
capabilities.change_password = ChangePasswordCapability {
enabled: true,
};
capabilities.set_avatar_url = SetAvatarUrlCapability {
enabled: true,
};
capabilities.set_displayname = SetDisplayNameCapability {
enabled: true,
};
// conduit does not implement 3PID stuff
capabilities.thirdparty_id_changes = ThirdPartyIdChangesCapability {
enabled: false,
+29 -32
View File
@@ -5,6 +5,7 @@ use ruma::{
},
events::{AnyGlobalAccountDataEventContent, AnyRoomAccountDataEventContent},
serde::Raw,
OwnedUserId, RoomId,
};
use serde::Deserialize;
use serde_json::{json, value::RawValue as RawJsonValue};
@@ -17,22 +18,7 @@ use crate::{services, Error, Result, Ruma};
pub(crate) async fn set_global_account_data_route(
body: Ruma<set_global_account_data::v3::Request>,
) -> Result<set_global_account_data::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let data: serde_json::Value = serde_json::from_str(body.data.json().get())
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?;
let event_type = body.event_type.to_string();
services().account_data.update(
None,
sender_user,
event_type.clone().into(),
&json!({
"type": event_type,
"content": data,
}),
)?;
set_account_data(None, &body.sender_user, &body.event_type.to_string(), body.data.json())?;
Ok(set_global_account_data::v3::Response {})
}
@@ -43,21 +29,11 @@ pub(crate) async fn set_global_account_data_route(
pub(crate) async fn set_room_account_data_route(
body: Ruma<set_room_account_data::v3::Request>,
) -> Result<set_room_account_data::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let data: serde_json::Value = serde_json::from_str(body.data.json().get())
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?;
let event_type = body.event_type.to_string();
services().account_data.update(
set_account_data(
Some(&body.room_id),
sender_user,
event_type.clone().into(),
&json!({
"type": event_type,
"content": data,
}),
&body.sender_user,
&body.event_type.to_string(),
body.data.json(),
)?;
Ok(set_room_account_data::v3::Response {})
@@ -74,7 +50,7 @@ pub(crate) async fn get_global_account_data_route(
let event: Box<RawJsonValue> = services()
.account_data
.get(None, sender_user, body.event_type.to_string().into())?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
let account_data = serde_json::from_str::<ExtractGlobalEventContent>(event.get())
.map_err(|_| Error::bad_database("Invalid account data event in db."))?
@@ -96,7 +72,7 @@ pub(crate) async fn get_room_account_data_route(
let event: Box<RawJsonValue> = services()
.account_data
.get(Some(&body.room_id), sender_user, body.event_type.clone())?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
let account_data = serde_json::from_str::<ExtractRoomEventContent>(event.get())
.map_err(|_| Error::bad_database("Invalid account data event in db."))?
@@ -107,6 +83,27 @@ pub(crate) async fn get_room_account_data_route(
})
}
fn set_account_data(
room_id: Option<&RoomId>, sender_user: &Option<OwnedUserId>, event_type: &str, data: &RawJsonValue,
) -> Result<()> {
let sender_user = sender_user.as_ref().expect("user is authenticated");
let data: serde_json::Value =
serde_json::from_str(data.get()).map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?;
services().account_data.update(
room_id,
sender_user,
event_type.into(),
&json!({
"type": event_type,
"content": data,
}),
)?;
Ok(())
}
#[derive(Deserialize)]
struct ExtractRoomEventContent {
content: Raw<AnyRoomAccountDataEventContent>,
+5 -7
View File
@@ -63,8 +63,8 @@ pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> R
lazy_loaded.insert(base_event.sender.as_str().to_owned());
}
// Use limit with maximum 100
let limit = u64::from(body.limit).min(100) as usize;
// Use limit or else 10, with maximum 100
let limit = usize::try_from(body.limit).unwrap_or(10).min(100);
let base_event = base_event.to_room_event();
@@ -163,7 +163,7 @@ pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> R
.map(|(_, pdu)| pdu.to_room_event())
.collect();
let mut state = Vec::new();
let mut state = Vec::with_capacity(state_ids.len());
for (shortstatekey, id) in state_ids {
let (event_type, state_key) = services()
@@ -188,14 +188,12 @@ pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> R
}
}
let resp = get_context::v3::Response {
Ok(get_context::v3::Response {
start: Some(start_token),
end: Some(end_token),
events_before,
event: Some(base_event),
events_after,
state,
};
Ok(resp)
})
}
+19 -9
View File
@@ -20,11 +20,11 @@ use ruma::{
},
StateEventType,
},
ServerName, UInt,
uint, ServerName, UInt,
};
use tracing::{error, info, warn};
use crate::{services, Error, Result, Ruma};
use crate::{service::server_is_ours, services, Error, Result, Ruma};
/// # `POST /_matrix/client/v3/publicRooms`
///
@@ -173,7 +173,7 @@ pub(crate) async fn get_room_visibility_route(
pub(crate) async fn get_public_rooms_filtered_helper(
server: Option<&ServerName>, limit: Option<UInt>, since: Option<&str>, filter: &Filter, _network: &RoomNetwork,
) -> Result<get_public_rooms_filtered::v3::Response> {
if let Some(other_server) = server.filter(|server| *server != services().globals.server_name().as_str()) {
if let Some(other_server) = server.filter(|server_name| !server_is_ours(server_name)) {
let response = services()
.sending
.send_federation_request(
@@ -198,8 +198,9 @@ pub(crate) async fn get_public_rooms_filtered_helper(
});
}
// Use limit or else 10, with maximum 100
let limit = limit.map_or(10, u64::from);
let mut num_since = 0_u64;
let mut num_since: u64 = 0;
if let Some(s) = &since {
let mut characters = s.chars();
@@ -363,12 +364,16 @@ pub(crate) async fn get_public_rooms_filtered_helper(
all_rooms.sort_by(|l, r| r.num_joined_members.cmp(&l.num_joined_members));
let total_room_count_estimate = (all_rooms.len() as u32).into();
let total_room_count_estimate = UInt::try_from(all_rooms.len()).unwrap_or_else(|_| uint!(0));
let chunk: Vec<_> = all_rooms
.into_iter()
.skip(num_since as usize)
.take(limit as usize)
.skip(
num_since
.try_into()
.expect("num_since should not be this high"),
)
.take(limit.try_into().expect("limit should not be this high"))
.collect();
let prev_batch = if num_since == 0 {
@@ -377,10 +382,15 @@ pub(crate) async fn get_public_rooms_filtered_helper(
Some(format!("p{num_since}"))
};
let next_batch = if chunk.len() < limit as usize {
let next_batch = if chunk.len() < limit.try_into().unwrap() {
None
} else {
Some(format!("n{}", num_since + limit))
Some(format!(
"n{}",
num_since
.checked_add(limit)
.expect("num_since and limit should not be that large")
))
};
Ok(get_public_rooms_filtered::v3::Response {
+17 -15
View File
@@ -1,4 +1,5 @@
use std::{
cmp,
collections::{hash_map, BTreeMap, HashMap, HashSet},
time::{Duration, Instant},
};
@@ -20,7 +21,12 @@ use serde_json::json;
use tracing::debug;
use super::SESSION_ID_LENGTH;
use crate::{services, utils, Error, Result, Ruma};
use crate::{
service::user_is_local,
services,
utils::{self},
Error, Result, Ruma,
};
/// # `POST /_matrix/client/r0/keys/upload`
///
@@ -71,24 +77,20 @@ pub(crate) async fn upload_keys_route(body: Ruma<upload_keys::v3::Request>) -> R
pub(crate) async fn get_keys_route(body: Ruma<get_keys::v3::Request>) -> Result<get_keys::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let response = get_keys_helper(
get_keys_helper(
Some(sender_user),
&body.device_keys,
|u| u == sender_user,
true, // Always allow local users to see device names of other local users
)
.await?;
Ok(response)
.await
}
/// # `POST /_matrix/client/r0/keys/claim`
///
/// Claims one-time keys
pub(crate) async fn claim_keys_route(body: Ruma<claim_keys::v3::Request>) -> Result<claim_keys::v3::Response> {
let response = claim_keys_helper(&body.one_time_keys).await?;
Ok(response)
claim_keys_helper(&body.one_time_keys).await
}
/// # `POST /_matrix/client/r0/keys/device_signing/upload`
@@ -260,7 +262,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
for (user_id, device_ids) in device_keys_input {
let user_id: &UserId = user_id;
if user_id.server_name() != services().globals.server_name() {
if !user_is_local(user_id) {
get_over_federation
.entry(user_id.server_name())
.or_insert_with(Vec::new)
@@ -338,7 +340,9 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
hash_map::Entry::Vacant(e) => {
e.insert((Instant::now(), 1));
},
hash_map::Entry::Occupied(mut e) => *e.get_mut() = (Instant::now(), e.get().1 + 1),
hash_map::Entry::Occupied(mut e) => {
*e.get_mut() = (Instant::now(), e.get().1.saturating_add(1));
},
}
};
@@ -353,10 +357,8 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
.get(server)
{
// Exponential backoff
let mut min_elapsed_duration = Duration::from_secs(5 * 60) * (*tries) * (*tries);
if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) {
min_elapsed_duration = Duration::from_secs(60 * 60 * 24);
}
const MAX_DURATION: Duration = Duration::from_secs(60 * 60 * 24);
let min_elapsed_duration = cmp::min(MAX_DURATION, Duration::from_secs(5 * 60) * (*tries) * (*tries));
if time.elapsed() < min_elapsed_duration {
debug!("Backing off query from {:?}", server);
@@ -454,7 +456,7 @@ pub(crate) async fn claim_keys_helper(
let mut get_over_federation = BTreeMap::new();
for (user_id, map) in one_time_keys_input {
if user_id.server_name() != services().globals.server_name() {
if !user_is_local(user_id) {
get_over_federation
.entry(user_id.server_name())
.or_insert_with(Vec::new)
+124 -32
View File
@@ -15,15 +15,27 @@ use webpage::HTML;
use crate::{
debug_warn,
service::media::{FileMeta, UrlPreviewData},
services, utils, Error, Result, Ruma, RumaResponse,
service::{
media::{FileMeta, UrlPreviewData},
server_is_ours,
},
services,
utils::{
self,
content_disposition::{
content_disposition_type, make_content_disposition, make_content_type, sanitise_filename,
},
},
Error, Result, Ruma, RumaResponse,
};
/// generated MXC ID (`media-id`) length
const MXC_LENGTH: usize = 32;
/// Cache control for immutable objects
const CACHE_CONTROL_IMMUTABLE: &str = "public, max-age=31536000, immutable";
const CACHE_CONTROL_IMMUTABLE: &str = "public,max-age=31536000,immutable";
const CORP_CROSS_ORIGIN: &str = "cross-origin";
/// # `GET /_matrix/media/v3/config`
///
@@ -119,6 +131,8 @@ pub(crate) async fn create_content_route(
utils::random_string(MXC_LENGTH)
);
let content_type = Some(make_content_type(&body.file, &body.content_type).to_owned());
services()
.media
.create(
@@ -126,17 +140,21 @@ pub(crate) async fn create_content_route(
mxc.clone(),
body.filename
.as_ref()
.map(|filename| "inline; filename=".to_owned() + filename)
.map(|filename| {
format!(
"{}; filename={}",
content_disposition_type(&body.file, &content_type),
sanitise_filename(filename.to_owned())
)
})
.as_deref(),
body.content_type.as_deref(),
content_type.as_deref(),
&body.file,
)
.await?;
let content_uri = mxc.into();
Ok(create_content::v3::Response {
content_uri,
content_uri: mxc.into(),
blurhash: None,
})
}
@@ -169,20 +187,23 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
if let Some(FileMeta {
content_disposition,
content_type,
file,
content_disposition,
}) = services().media.get(mxc.clone()).await?
{
let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition, None));
let content_type = Some(make_content_type(&file, &content_type).to_owned());
Ok(get_content::v3::Response {
file,
content_type,
content_disposition,
cross_origin_resource_policy: Some("cross-origin".to_owned()),
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
})
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
get_remote_content(
} else if !server_is_ours(&body.server_name) && body.allow_remote {
let response = get_remote_content(
&mxc,
&body.server_name,
body.media_id.clone(),
@@ -193,6 +214,22 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
.map_err(|e| {
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
Error::BadRequest(ErrorKind::NotFound, "Remote media error.")
})?;
let content_disposition = Some(make_content_disposition(
&response.file,
&response.content_type,
response.content_disposition,
None,
));
let content_type = Some(make_content_type(&response.file, &response.content_type).to_owned());
Ok(get_content::v3::Response {
file: response.file,
content_type,
content_disposition,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
})
} else {
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
@@ -233,17 +270,25 @@ pub(crate) async fn get_content_as_filename_route(
if let Some(FileMeta {
content_type,
file,
..
content_disposition,
}) = services().media.get(mxc.clone()).await?
{
let content_disposition = Some(make_content_disposition(
&file,
&content_type,
content_disposition,
Some(body.filename.clone()),
));
let content_type = Some(make_content_type(&file, &content_type).to_owned());
Ok(get_content_as_filename::v3::Response {
file,
content_type,
content_disposition: Some(format!("inline; filename={}", body.filename)),
cross_origin_resource_policy: Some("cross-origin".to_owned()),
content_disposition,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
})
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
} else if !server_is_ours(&body.server_name) && body.allow_remote {
match get_remote_content(
&mxc,
&body.server_name,
@@ -253,13 +298,25 @@ pub(crate) async fn get_content_as_filename_route(
)
.await
{
Ok(remote_content_response) => Ok(get_content_as_filename::v3::Response {
content_disposition: Some(format!("inline: filename={}", body.filename)),
content_type: remote_content_response.content_type,
file: remote_content_response.file,
cross_origin_resource_policy: Some("cross-origin".to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
}),
Ok(remote_content_response) => {
let content_disposition = Some(make_content_disposition(
&remote_content_response.file,
&remote_content_response.content_type,
remote_content_response.content_disposition,
None,
));
let content_type = Some(
make_content_type(&remote_content_response.file, &remote_content_response.content_type).to_owned(),
);
Ok(get_content_as_filename::v3::Response {
content_disposition,
content_type,
file: remote_content_response.file,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
})
},
Err(e) => {
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
Err(Error::BadRequest(ErrorKind::NotFound, "Remote media error."))
@@ -304,7 +361,7 @@ pub(crate) async fn get_content_thumbnail_route(
if let Some(FileMeta {
content_type,
file,
..
content_disposition,
}) = services()
.media
.get_thumbnail(
@@ -318,17 +375,21 @@ pub(crate) async fn get_content_thumbnail_route(
)
.await?
{
let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition, None));
let content_type = Some(make_content_type(&file, &content_type).to_owned());
Ok(get_content_thumbnail::v3::Response {
file,
content_type,
cross_origin_resource_policy: Some("cross-origin".to_owned()),
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
content_disposition,
})
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
} else if !server_is_ours(&body.server_name) && body.allow_remote {
if services()
.globals
.prevent_media_downloads_from()
.contains(&body.server_name.clone())
.contains(&body.server_name)
{
// we'll lie to the client and say the blocked server's media was not found and
// log. the client has no way of telling anyways so this is a security bonus.
@@ -367,7 +428,23 @@ pub(crate) async fn get_content_thumbnail_route(
)
.await?;
Ok(get_thumbnail_response)
let content_disposition = Some(make_content_disposition(
&get_thumbnail_response.file,
&get_thumbnail_response.content_type,
get_thumbnail_response.content_disposition,
None,
));
let content_type = Some(
make_content_type(&get_thumbnail_response.file, &get_thumbnail_response.content_type).to_owned(),
);
Ok(get_content_thumbnail::v3::Response {
file: get_thumbnail_response.file,
content_type,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
content_disposition,
})
},
Err(e) => {
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
@@ -407,7 +484,7 @@ async fn get_remote_content(
{
// we'll lie to the client and say the blocked server's media was not found and
// log. the client has no way of telling anyways so this is a security bonus.
debug_warn!("Received request for media `{}` on blocklisted server", mxc);
debug_warn!("Received request for media `{mxc}` on blocklisted server");
return Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."));
}
@@ -425,18 +502,33 @@ async fn get_remote_content(
)
.await?;
let content_disposition = Some(make_content_disposition(
&content_response.file,
&content_response.content_type,
content_response.content_disposition,
None,
));
let content_type = Some(make_content_type(&content_response.file, &content_response.content_type).to_owned());
services()
.media
.create(
None,
mxc.to_owned(),
content_response.content_disposition.as_deref(),
content_response.content_type.as_deref(),
content_disposition.as_deref(),
content_type.as_deref(),
&content_response.file,
)
.await?;
Ok(content_response)
Ok(get_content::v3::Response {
file: content_response.file,
content_type,
content_disposition,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
})
}
async fn download_image(client: &reqwest::Client, url: &str) -> Result<UrlPreviewData> {
+132 -201
View File
@@ -1,4 +1,5 @@
use std::{
cmp,
collections::{hash_map::Entry, BTreeMap, HashMap, HashSet},
sync::Arc,
time::{Duration, Instant},
@@ -20,12 +21,13 @@ use ruma::{
room::{
join_rules::{AllowRule, JoinRule, RoomJoinRulesEventContent},
member::{MembershipState, RoomMemberEventContent},
message::RoomMessageEventContent,
},
StateEventType, TimelineEventType,
},
serde::Base64,
state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId, OwnedServerName,
OwnedUserId, RoomId, RoomVersionId, UserId,
OwnedUserId, RoomId, RoomVersionId, ServerName, UserId,
};
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
use tokio::sync::RwLock;
@@ -33,10 +35,104 @@ use tracing::{debug, error, info, trace, warn};
use super::get_alias_helper;
use crate::{
service::pdu::{gen_event_id_canonical_json, PduBuilder},
services, utils, Error, PduEvent, Result, Ruma,
service::{
pdu::{gen_event_id_canonical_json, PduBuilder},
server_is_ours, user_is_local,
},
services,
utils::{self},
Error, PduEvent, Result, Ruma,
};
/// Checks if the room is banned in any way possible and the sender user is not
/// an admin.
///
/// Performs automatic deactivation if `auto_deactivate_banned_room_attempts` is
/// enabled
#[tracing::instrument]
async fn banned_room_check(user_id: &UserId, room_id: Option<&RoomId>, server_name: Option<&ServerName>) -> Result<()> {
if !services().users.is_admin(user_id)? {
if let Some(room_id) = room_id {
if services().rooms.metadata.is_banned(room_id)?
|| services()
.globals
.config
.forbidden_remote_server_names
.contains(&room_id.server_name().unwrap().to_owned())
{
warn!(
"User {user_id} who is not an admin attempted to send an invite for or attempted to join a banned \
room or banned room server name: {room_id}."
);
if services()
.globals
.config
.auto_deactivate_banned_room_attempts
{
warn!("Automatically deactivating user {user_id} due to attempted banned room join");
services()
.admin
.send_message(RoomMessageEventContent::text_plain(format!(
"Automatically deactivating user {user_id} due to attempted banned room join"
)))
.await;
// ignore errors
leave_all_rooms(user_id).await;
if let Err(e) = services().users.deactivate_account(user_id) {
warn!(%e, "Failed to deactivate account");
}
}
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This room is banned on this homeserver.",
));
}
} else if let Some(server_name) = server_name {
if services()
.globals
.config
.forbidden_remote_server_names
.contains(&server_name.to_owned())
{
warn!(
"User {user_id} who is not an admin tried joining a room which has the server name {server_name} \
that is globally forbidden. Rejecting.",
);
if services()
.globals
.config
.auto_deactivate_banned_room_attempts
{
warn!("Automatically deactivating user {user_id} due to attempted banned room join");
services()
.admin
.send_message(RoomMessageEventContent::text_plain(format!(
"Automatically deactivating user {user_id} due to attempted banned room join"
)))
.await;
// ignore errors
leave_all_rooms(user_id).await;
if let Err(e) = services().users.deactivate_account(user_id) {
warn!(%e, "Failed to deactivate account");
}
}
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This remote server is banned on this homeserver.",
));
}
}
}
Ok(())
}
/// # `POST /_matrix/client/r0/rooms/{roomId}/join`
///
/// Tries to join the sender user into a room.
@@ -50,32 +146,7 @@ pub(crate) async fn join_room_by_id_route(
) -> Result<join_room_by_id::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if services().rooms.metadata.is_banned(&body.room_id)? && !services().users.is_admin(sender_user)? {
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This room is banned on this homeserver.",
));
}
if let Some(server) = body.room_id.server_name() {
if services()
.globals
.config
.forbidden_remote_server_names
.contains(&server.to_owned())
&& !services().users.is_admin(sender_user)?
{
warn!(
"User {sender_user} tried joining room ID {} which has a server name that is globally forbidden. \
Rejecting.",
body.room_id
);
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This remote server is banned on this homeserver.",
));
}
}
banned_room_check(sender_user, Some(&body.room_id), body.room_id.server_name()).await?;
// There is no body.server_name for /roomId/join
let mut servers = services()
@@ -128,31 +199,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) {
Ok(room_id) => {
if services().rooms.metadata.is_banned(&room_id)? && !services().users.is_admin(sender_user)? {
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This room is banned on this homeserver.",
));
}
if let Some(server) = room_id.server_name() {
if services()
.globals
.config
.forbidden_remote_server_names
.contains(&server.to_owned())
&& !services().users.is_admin(sender_user)?
{
warn!(
"User {sender_user} tried joining room ID {room_id} which has a server name that is globally \
forbidden. Rejecting.",
);
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This remote server is banned on this homeserver.",
));
}
}
banned_room_check(sender_user, Some(&room_id), room_id.server_name()).await?;
let mut servers = body.server_name.clone();
servers.extend(
@@ -183,69 +230,9 @@ pub(crate) async fn join_room_by_id_or_alias_route(
(servers, room_id)
},
Err(room_alias) => {
if services()
.globals
.config
.forbidden_remote_server_names
.contains(&room_alias.server_name().to_owned())
&& !services().users.is_admin(sender_user)?
{
warn!(
"User {sender_user} tried joining room alias {room_alias} which has a server name that is \
globally forbidden. Rejecting.",
);
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This remote server is banned on this homeserver.",
));
}
let response = get_alias_helper(room_alias.clone(), Some(body.server_name.clone())).await?;
if services().rooms.metadata.is_banned(&response.room_id)? && !services().users.is_admin(sender_user)? {
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This room is banned on this homeserver.",
));
}
if services()
.globals
.config
.forbidden_remote_server_names
.contains(&room_alias.server_name().to_owned())
&& !services().users.is_admin(sender_user)?
{
warn!(
"User {sender_user} tried joining room alias {room_alias} with room ID {}, which the alias has a \
server name that is globally forbidden. Rejecting.",
&response.room_id
);
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This remote server is banned on this homeserver.",
));
}
if let Some(server) = response.room_id.server_name() {
if services()
.globals
.config
.forbidden_remote_server_names
.contains(&server.to_owned())
&& !services().users.is_admin(sender_user)?
{
warn!(
"User {sender_user} tried joining room alias {room_alias} with room ID {}, which has a server \
name that is globally forbidden. Rejecting.",
&response.room_id
);
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This remote server is banned on this homeserver.",
));
}
}
banned_room_check(sender_user, Some(&response.room_id), Some(room_alias.server_name())).await?;
let mut servers = body.server_name;
servers.extend(response.servers);
@@ -318,30 +305,7 @@ pub(crate) async fn invite_user_route(body: Ruma<invite_user::v3::Request>) -> R
));
}
if services().rooms.metadata.is_banned(&body.room_id)? && !services().users.is_admin(sender_user)? {
info!(
"Local user {} who is not an admin attempted to send an invite for banned room {}.",
&sender_user, &body.room_id
);
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This room is banned on this homeserver.",
));
}
if let Some(server) = body.room_id.server_name() {
if services()
.globals
.config
.forbidden_remote_server_names
.contains(&server.to_owned())
{
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"Server is banned on this homeserver.",
));
}
}
banned_room_check(sender_user, Some(&body.room_id), body.room_id.server_name()).await?;
if let invite_user::v3::InvitationRecipient::UserId {
user_id,
@@ -360,15 +324,6 @@ pub(crate) async fn invite_user_route(body: Ruma<invite_user::v3::Request>) -> R
pub(crate) async fn kick_user_route(body: Ruma<kick_user::v3::Request>) -> Result<kick_user::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if let Ok(true) = services()
.rooms
.state_cache
.is_left(sender_user, &body.room_id)
{
info!("{} is not in room {}", &body.user_id, &body.room_id);
return Ok(kick_user::v3::Response {});
}
let mut event: RoomMemberEventContent = serde_json::from_str(
services()
.rooms
@@ -425,17 +380,6 @@ pub(crate) async fn kick_user_route(body: Ruma<kick_user::v3::Request>) -> Resul
pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<ban_user::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if let Ok(Some(membership_event)) = services()
.rooms
.state_accessor
.get_member(&body.room_id, sender_user)
{
if membership_event.membership == MembershipState::Ban {
info!("{} is already banned in {}", &body.user_id, &body.room_id);
return Ok(ban_user::v3::Response {});
}
}
let event = services()
.rooms
.state_accessor
@@ -443,11 +387,11 @@ pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<
.map_or(
Ok(RoomMemberEventContent {
membership: MembershipState::Ban,
displayname: services().users.displayname(&body.user_id)?,
avatar_url: services().users.avatar_url(&body.user_id)?,
displayname: None,
avatar_url: None,
is_direct: None,
third_party_invite: None,
blurhash: services().users.blurhash(&body.user_id)?,
blurhash: services().users.blurhash(&body.user_id).unwrap_or_default(),
reason: body.reason.clone(),
join_authorized_via_users_server: None,
}),
@@ -455,14 +399,8 @@ pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<
serde_json::from_str(event.content.get())
.map(|event: RoomMemberEventContent| RoomMemberEventContent {
membership: MembershipState::Ban,
displayname: services()
.users
.displayname(&body.user_id)
.unwrap_or_default(),
avatar_url: services()
.users
.avatar_url(&body.user_id)
.unwrap_or_default(),
displayname: None,
avatar_url: None,
blurhash: services().users.blurhash(&body.user_id).unwrap_or_default(),
reason: body.reason.clone(),
join_authorized_via_users_server: None,
@@ -511,17 +449,6 @@ pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<
pub(crate) async fn unban_user_route(body: Ruma<unban_user::v3::Request>) -> Result<unban_user::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if let Ok(Some(membership_event)) = services()
.rooms
.state_accessor
.get_member(&body.room_id, sender_user)
{
if membership_event.membership != MembershipState::Ban {
info!("{} is already unbanned in {}", &body.user_id, &body.room_id);
return Ok(unban_user::v3::Response {});
}
}
let mut event: RoomMemberEventContent = serde_json::from_str(
services()
.rooms
@@ -687,7 +614,7 @@ pub(crate) async fn joined_members_route(
})
}
pub(crate) async fn join_room_by_id_helper(
pub async fn join_room_by_id_helper(
sender_user: Option<&UserId>, room_id: &RoomId, reason: Option<String>, servers: &[OwnedServerName],
_third_party_signed: Option<&ThirdPartySigned>,
) -> Result<join_room_by_id::v3::Response> {
@@ -1088,7 +1015,7 @@ pub(crate) async fn join_room_by_id_helper(
.state_cache
.room_members(room_id)
.filter_map(Result::ok)
.filter(|user| user.server_name() == services().globals.server_name())
.filter(|user| user_is_local(user))
.collect::<Vec<OwnedUserId>>();
let mut authorized_user: Option<OwnedUserId> = None;
@@ -1150,7 +1077,7 @@ pub(crate) async fn join_room_by_id_helper(
if !restriction_rooms.is_empty()
&& servers
.iter()
.any(|s| *s != services().globals.server_name())
.any(|server_name| !server_is_ours(server_name))
{
info!(
"We couldn't do the join locally, maybe federation can help to satisfy the restricted join \
@@ -1299,11 +1226,11 @@ async fn make_join_request(
) -> Result<(federation::membership::prepare_join_event::v1::Response, OwnedServerName)> {
let mut make_join_response_and_server = Err(Error::BadServerResponse("No server available to assist in joining."));
let mut make_join_counter = 0;
let mut incompatible_room_version_count = 0;
let mut make_join_counter: u16 = 0;
let mut incompatible_room_version_count: u8 = 0;
for remote_server in servers {
if remote_server == services().globals.server_name() {
if server_is_ours(remote_server) {
continue;
}
info!("Asking {remote_server} for make_join ({make_join_counter})");
@@ -1320,7 +1247,7 @@ async fn make_join_request(
.await;
trace!("make_join response: {:?}", make_join_response);
make_join_counter += 1;
make_join_counter = make_join_counter.saturating_add(1);
if let Err(ref e) = make_join_response {
trace!("make_join ErrorKind string: {:?}", e.error_code().to_string());
@@ -1334,7 +1261,7 @@ async fn make_join_request(
.to_string()
.contains("M_UNSUPPORTED_ROOM_VERSION")
{
incompatible_room_version_count += 1;
incompatible_room_version_count = incompatible_room_version_count.saturating_add(1);
}
if incompatible_room_version_count > 15 {
@@ -1391,7 +1318,9 @@ async fn validate_and_add_event_id(
Entry::Vacant(e) => {
e.insert((Instant::now(), 1));
},
Entry::Occupied(mut e) => *e.get_mut() = (Instant::now(), e.get().1 + 1),
Entry::Occupied(mut e) => {
*e.get_mut() = (Instant::now(), e.get().1.saturating_add(1));
},
}
};
@@ -1403,10 +1332,8 @@ async fn validate_and_add_event_id(
.get(&event_id)
{
// Exponential backoff
let mut min_elapsed_duration = Duration::from_secs(5 * 60) * (*tries) * (*tries);
if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) {
min_elapsed_duration = Duration::from_secs(60 * 60 * 24);
}
const MAX_DURATION: Duration = Duration::from_secs(60 * 60 * 24);
let min_elapsed_duration = cmp::min(MAX_DURATION, Duration::from_secs(5 * 60) * (*tries) * (*tries));
if time.elapsed() < min_elapsed_duration {
debug!("Backing off from {}", event_id);
@@ -1436,7 +1363,7 @@ pub(crate) async fn invite_helper(
));
}
if user_id.server_name() != services().globals.server_name() {
if !user_is_local(user_id) {
let (pdu, pdu_json, invite_room_state) = {
let mutex_state = Arc::clone(
services()
@@ -1603,8 +1530,9 @@ pub(crate) async fn invite_helper(
Ok(())
}
// Make a user leave all their joined rooms
pub(crate) async fn leave_all_rooms(user_id: &UserId) -> Result<()> {
// Make a user leave all their joined rooms, forgets all rooms, and ignores
// errors
pub async fn leave_all_rooms(user_id: &UserId) {
let all_rooms = services()
.rooms
.state_cache
@@ -1624,13 +1552,16 @@ pub(crate) async fn leave_all_rooms(user_id: &UserId) -> Result<()> {
};
// ignore errors
_ = leave_room(user_id, &room_id, None).await;
if let Err(e) = services().rooms.state_cache.forget(&room_id, user_id) {
warn!(%e, "Failed to forget room");
}
if let Err(e) = leave_room(user_id, &room_id, None).await {
warn!(%e, "Failed to leave room");
}
}
Ok(())
}
pub(crate) async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> {
pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> {
// Ask a remote server if we don't have this room
if !services()
.rooms
+7 -9
View File
@@ -3,6 +3,7 @@ use std::{
sync::Arc,
};
use conduit::PduCount;
use ruma::{
api::client::{
error::ErrorKind,
@@ -14,10 +15,7 @@ use ruma::{
};
use serde_json::{from_str, Value};
use crate::{
service::{pdu::PduBuilder, rooms::timeline::PduCount},
services, utils, Error, PduEvent, Result, Ruma,
};
use crate::{service::pdu::PduBuilder, services, utils, Error, PduEvent, Result, Ruma};
/// # `PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}`
///
@@ -144,7 +142,7 @@ pub(crate) async fn get_message_events_route(
.lazy_load_confirm_delivery(sender_user, sender_device, &body.room_id, from)
.await?;
let limit = u64::from(body.limit).min(100) as usize;
let limit = usize::try_from(body.limit).unwrap_or(10).min(100);
let next_token;
@@ -159,8 +157,9 @@ pub(crate) async fn get_message_events_route(
.timeline
.pdus_after(sender_user, &body.room_id, from)?
.filter_map(Result::ok) // Filter out buggy events
.filter(|(_, pdu)| contains_url_filter(pdu, &body.filter))
.filter(|(_, pdu)| visibility_filter(pdu, sender_user, &body.room_id))
.filter(|(_, pdu)| { contains_url_filter(pdu, &body.filter) && visibility_filter(pdu, sender_user, &body.room_id)
})
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
.take(limit)
.collect();
@@ -205,8 +204,7 @@ pub(crate) async fn get_message_events_route(
.timeline
.pdus_until(sender_user, &body.room_id, from)?
.filter_map(Result::ok) // Filter out buggy events
.filter(|(_, pdu)| contains_url_filter(pdu, &body.filter))
.filter(|(_, pdu)| visibility_filter(pdu, sender_user, &body.room_id))
.filter(|(_, pdu)| {contains_url_filter(pdu, &body.filter) && visibility_filter(pdu, sender_user, &body.room_id)})
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
.take(limit)
.collect();
+38 -39
View File
@@ -1,40 +1,41 @@
mod account;
mod alias;
mod backup;
mod capabilities;
mod config;
mod context;
mod device;
mod directory;
mod filter;
mod keys;
mod media;
mod membership;
mod message;
mod presence;
mod profile;
mod push;
mod read_marker;
mod redact;
mod relations;
mod report;
mod room;
mod search;
mod session;
mod space;
mod state;
mod sync;
mod tag;
mod thirdparty;
mod threads;
mod to_device;
mod typing;
mod unstable;
mod unversioned;
mod user_directory;
mod voip;
pub(crate) mod account;
pub(crate) mod alias;
pub(crate) mod backup;
pub(crate) mod capabilities;
pub(crate) mod config;
pub(crate) mod context;
pub(crate) mod device;
pub(crate) mod directory;
pub(crate) mod filter;
pub(crate) mod keys;
pub(crate) mod media;
pub(crate) mod membership;
pub(crate) mod message;
pub(crate) mod presence;
pub(crate) mod profile;
pub(crate) mod push;
pub(crate) mod read_marker;
pub(crate) mod redact;
pub(crate) mod relations;
pub(crate) mod report;
pub(crate) mod room;
pub(crate) mod search;
pub(crate) mod session;
pub(crate) mod space;
pub(crate) mod state;
pub(crate) mod sync;
pub(crate) mod tag;
pub(crate) mod thirdparty;
pub(crate) mod threads;
pub(crate) mod to_device;
pub(crate) mod typing;
pub(crate) mod unstable;
pub(crate) mod unversioned;
pub(crate) mod user_directory;
pub(crate) mod voip;
pub(crate) use account::*;
pub use alias::get_alias_helper;
pub(crate) use alias::*;
pub(crate) use backup::*;
pub(crate) use capabilities::*;
@@ -46,6 +47,7 @@ pub(crate) use filter::*;
pub(crate) use keys::*;
pub(crate) use media::*;
pub(crate) use membership::*;
pub use membership::{join_room_by_id_helper, leave_all_rooms, leave_room};
pub(crate) use message::*;
pub(crate) use presence::*;
pub(crate) use profile::*;
@@ -77,7 +79,4 @@ const DEVICE_ID_LENGTH: usize = 10;
const TOKEN_LENGTH: usize = 32;
/// generated user session ID length
pub(crate) const SESSION_ID_LENGTH: usize = 32;
/// auto-generated password length
pub(crate) const AUTO_GEN_PASSWORD_LENGTH: usize = 25;
const SESSION_ID_LENGTH: usize = service::uiaa::SESSION_ID_LENGTH;
+13 -2
View File
@@ -42,16 +42,27 @@ pub(crate) async fn get_presence_route(body: Ruma<get_presence::v3::Request>) ->
.user
.get_shared_rooms(vec![sender_user.clone(), body.user_id.clone()])?
{
if let Some(presence) = services().presence.get_presence(sender_user)? {
if let Some(presence) = services().presence.get_presence(&body.user_id)? {
presence_event = Some(presence);
break;
}
}
if let Some(presence) = presence_event {
let status_msg = if presence
.content
.status_msg
.as_ref()
.is_some_and(String::is_empty)
{
None
} else {
presence.content.status_msg
};
Ok(get_presence::v3::Response {
// TODO: Should ruma just use the presenceeventcontent type here?
status_msg: presence.content.status_msg,
status_msg,
currently_active: presence.content.currently_active,
last_active_ago: presence
.content

Some files were not shown because too many files have changed in this diff Show More