Skip to content

Releases: matrix-construct/tuwunel

Release v1.9.0

Choose a tag to compare

@github-actions github-actions released this 19 Aug 00:49

Tuwunel 1.9.0

August 18, 2026

New Features & Enhancements

  • URL previews are richer and more reliable, shipped by @x86pup. The fetch budget rises from 256,000 to 786,432 bytes, each request gets its own User-Agent, and oEmbed recovers pages that otherwise yield nothing. YouTube links now show their title, channel and thumbnail instead of a bare hostname. Previews remain disabled until at least one url_preview_*_allowlist option is non-empty. See docs/media/url-previews.md.

  • TLS now uses aws-lc-rs and the platform trust store, courtesy of @dasha-uwu. ring leaves the build, while SMTP, LDAPS, S3 media, client and federation pools share one crypto provider. allow_invalid_tls_certificates now covers LDAP TLS too. Operators running the published standalone binary in a chroot, distroless image or hand-rolled FROM scratch image must install a CA bundle or set SSL_CERT_FILE before upgrading. Unlike 1.8.3, version 1.9.0 will not start without one. The container image and both packages already include a bundle.

  • Push rules can now match related events (MSC3664), graciously contributed by @x86pup and raised by @OxiOverlord in (#544). The default .im.nheko.msc3664.reply rule is added to every account on upgrade regardless of configuration, so nheko users get reply notifications immediately through client-side evaluation. Server-side evaluation for notification counts and pusher delivery requires msc3664_related_event_match, which defaults to false. The default covers replies; .m.rule.reaction still suppresses reactions, but custom rules can now match m.annotation.

  • Tuwunel can now rewrite your configuration file. The new command-line regeneration mode and !admin server regenerate-config render the example document from the live schema with your values, canonical option names, and unknown keys preserved under # DEPRECATED, # UNKNOWN or # UNDOCUMENTED. Regenerate and diff to review the result. Nothing is applied or reloaded, and the default output is a new file beside the original. A separate mode emits a clean example. See docs/configuration/regeneration.md.

  • Migrating a conduwuit-lineage database now preserves account state, with appreciation to @x86pup. On the next boot, passwordless accounts no longer appear deactivated, deactivated accounts remain inactive with their old passwords disabled, and email addresses keep their owners.

  • !admin query oauth adopt <provider_id> converts a migrated conduwuit-lineage database's OIDC subject map into durable identity associations in one pass. Without it, each migrated user's next login creates a fresh account with a generated localpart.

  • Thanks to @x86pup, FreeBSD and OpenBSD arm64 build without custom CXXFLAGS, while NetBSD arm64 selects hardware CRC32C at runtime. New guides cover FreeBSD, NetBSD, OpenBSD, RISC-V 64 and 32-bit ARM, each with a verified build and its remaining limits: OpenBSD needs Rust from -current, ARMv6 lacks a working C++ runtime here, and none of the five is covered by CI or ships an artifact.

  • Nine cache-capacity options that previously read "This item is undocumented" now explain that their values are entry counts, not byte sizes, graciously contributed by @byteflavour in (#541).

  • Startup now warns when write-ahead log preallocation is enabled on btrfs or ZFS, where it reserves much more disk than the logs use. Raised by @byteflavour in (#535). rocksdb_allow_fallocate still defaults to true; the server warns without changing the engine setting.

  • Thanks to @x86pup, each account's push ruleset is capped at 10,000 rules, with each serialized rule capped at 1024 bytes. The limits apply only when the ruleset grows; deletes and disables remain unconditional.

  • force_migration is now hidden from generated configuration. If it is still set at boot, Tuwunel warns and waits fifteen seconds. Tip of the hat to @x86pup.

  • With appreciation to @x86pup, the .deb and .rpm install checks now resolve their dependencies, and the .deb check confirms that the programs invoked by the unit exist.

  • Credit to @dasha-uwu for reworking registration-token handling and correcting two admin API responses: deleting a config-file token now returns 403 instead of 500, and updating one returns 403 instead of 404.

  • RocksDB advances to 11.8.1 and jemalloc to 5.3.1-2. Eleven low-traffic cache columns now share a pool, while five hot columns retain dedicated pools sized by four new options. Failed write-ahead log flushes and syncs are now logged.

  • Fifteen subsystems moved their multi-row writes into single database transactions, so a related group of rows is either all visible or none.

  • One option added in 1.8.3 went undocumented. refresh_token_reuse_grace controls how long a rotated refresh token remains valid; setting it to 0 treats any reuse as a compromise. The option is not new, and its behavior is unchanged in this release.

  • The Matrix Authentication Service guide now distinguishes MAS as a login provider from MAS as a provisioner and lists all twelve provisioning routes.

  • The reverse-proxy guide now correctly explains that the /_synapse/admin/v1/register pair uses an HMAC over registration_shared_secret, not an admin token. Restrict this path to trusted networks.

  • Internal Rust API documentation now covers the public surfaces of tuwunel-core and the database crate. Complement gains shared-secret registration and an advanced fork pin. Dependencies also advance, including h2 0.4.16 for RUSTSEC-2026-0258.

Bug Fixes

  • LDAP logins broken by 1.8.3 work again. That release rejected binds for existing local accounts without LDAP origin, locking out deployments that added LDAP after creating accounts. The gate is reverted (bf1ba04), with only its deactivation check restored (b1eee03). Before upgrading, confirm that [global.ldap] base_dn and filter, or the bind_dn subtree in direct-bind mode when no search runs, resolve only principals allowed to log in as that localpart. If admin_filter is set, a directory entry can now promote an existing local account to admin. Use !admin query users search-ldap on 1.8.3 to check. Sincere apologies to everyone locked out by 1.8.3.

  • @dasha-uwu removed the LDAP name_attribute option and stopped directory searches by givenName; an entry must put the Matrix localpart in uid_attribute to allow login. A leftover [global.ldap] name_attribute produces no boot warning. Regenerate and diff the configuration to reveal it as # DEPRECATED, then remove it manually.

  • Push notifications reach gateways again, and failures are now retried. Rejected pushes were counted as delivered and deleted, leaving nothing to retry and permanently sidelining the pushkey. Tuwunel now deletes only accepted events, arms the destination's retry timer, and logs the user, pushkey and error chain on failure. Reported by @xcysy32 in (#543), which remains open pending confirmation. Thanks also to @NekoCWD for separating the proxy case into (#554).

  • Three related push defects are also fixed: rules combining notify with historical actions the spec says to ignore are accepted; gateway path stripping now matches only at the URL's end; and pushes during shutdown no longer panic.

  • Thanks to @haydonryan for reporting the stale iOS unread badge in (#538) and fixing it in (#539). Read advances refreshed pushers only when the stored count cleared from nonzero, so a badge left nonzero after server counts reached zero never received the explicit zero. Refresh is now unconditional, bounded by each pusher's record of the last count the gateway accepted. Leaving or being banned from an unread room updates it too.

  • A one-time migration repairs two kinds of latent database damage on the first 1.9.0 boot. Releases before 1.8.3 could assign two short ids to one identity, leaving state entries that later removal could not cancel. Every release through 1.8.3 also cached auth chains truncated when an ancestor was missing, a normal backfill gap. Those chains are cleared once, and incomplete walks are now marked. A narrower cross-room case is a genuine regression first shipped in 1.5.0 (944f165).

  • Expect a longer first boot before the listener opens while a clean database performs four full column scans once. The duration is unknown; this was found internally and not reported by a user.

  • A federation disclosure is fixed. /state, /state_ids and /event_auth looked up an event by id alone, allowing a server that held any event id from a room it had never been in, and could pass the access check on any other room here, to read the first room's state through the second room's URL. Tuwunel now validates the stored event's room, and wrong-room responses are byte-identical to missing-event responses. The issue dates to the server ACL implementation in early 2022.

  • timestamp_to_event previously ran only an ACL check, letting any server we federate with obtain a real event id from a room it had not joined and satisfying the disclosure's precondition. It now runs the shared access check.

  • Federated events that authorize at their own position but not against current room state are now soft-failed and re-examined on a widening schedule, as the spec requires, instead of being permanently rejected.

  • Remote joins and inbound transactions acquired the room-state and federation mutexes in opposite orders, so joining an active room could freeze it until timeout. The order is now consistent everywhere.

  • The get_missing_events walk now has its own bound, preventing a large response limit from forcing unbounded timeline lookups. Ingest also no longer panics when a signed event's auth_events names a non-state event we already hold; the caught panic previously returned 500.

  • Element X can again preview or open an unjoined room, fixing the retry-dialog loop reported by @utop-top in (#...

Read more

Release v1.8.3

Choose a tag to compare

@github-actions github-actions released this 05 Aug 23:27

Tuwunel 1.8.3

August 5, 2026

New Features & Enhancements

  • QR-code login πŸ“² Take a picture of Element Web with Element X: login instantly. (MSC4108). Rendezvous sessions are served over the MSC4388 transport, with bounded in-memory payloads, a session cap and TTL, and their own rate limit. The native OIDC server grows to meet it, serving account login for the device authorization grant and account management with no identity provider configured at all. Raised by @rjwalters in (#525).

  • Video thumbnails. A client that uploads a video without a thumbnail left the server nothing to preview, so media_video_thumbnail_command names a program that extracts one still frame for the image thumbnailer to scale and crop. Inspired by @az4521 in (#397) and implemented by @x86pup, who also caps what the thumbnailer will decode at all with media_thumbnail_max_pixels, checked against the image header before any decoder allocates. Requests whose thumbnail would only reproduce the source skip generation entirely, shipped by @lhjt in (#521).

  • Native journald logging. Under systemd every line landed in the journal at info priority, so priority filtering in journalctl was blind to Tuwunel's warnings and errors. Events are now submitted natively with their severity, target, and source location, under a log_journald option that defaults on. Shipped by @byteflavour in (#534) and extended by @x86pup, whose layer streams the console-formatted line into each entry and records tracing fields under an F_ prefix, so the journal can be filtered by room or event id.

  • systemd socket activation and configuration reload, courtesy of @x86pup. A socket unit ships for activation, systemctl reload is wired into the Debian and Red Hat units, the Arch unit refreshes its systemd credentials on reload, and a reload now replays the startup command line, so a server started with -c or -O rebuilds the configuration it actually had. TURN and registration secrets are re-read at each use, so rotating either file no longer needs a restart, and an in-place restart strips the activation variables it can no longer honor. Documented alongside.

  • Android builds work, courtesy of @x86pup. The TLS segment is aligned for bionic's loader, SMTP and client TLS verify against bundled webpki roots, dns_servers becomes a requirement where the system resolver would need a JVM, and jemalloc and rust-rocksdb are advanced for the libgcc fix.

  • LDAP login hardening from @x86pup. Filter and bind-DN metacharacters in the login localpart are escaped, an empty password is rejected before the bind is attempted, accounts that did not originate in LDAP and deactivated accounts are refused, and error handling, timeouts, and password-file IO are tightened.

  • Appservices gain third-party network lookup forwarding: the client /thirdparty/* endpoints fan out to every registered bridge, carrying protocol metadata verbatim. One-time keys are proxied ahead of the fallback key and appservice device keys are overlaid across client and federation paths (MSC3983, MSC3984). A stalled transaction is retried after a ping, and appservice users are excluded from user-directory search.

  • Push badges carry the account-wide unread count, derived across joined rooms at delivery, refreshed on demand, recovered from the queue at startup, and hooked into the read paths that change it. An explicit zero is preserved and only a true opt-out is honored. Reported and diagnosed by @lhjt.

  • State resolution gets a reworked conflicted-subgraph walker, an event-ID sha256 codec with a matching map hasher, mainline positions read from a map, an auth difference hashed by event ID, and short hashes persisted with their state diffs.

  • A Matrix Authentication Service provider guide documents the integration end to end, with a startup warning when mas_secret is set but no MAS identity provider is configured.

  • @dasha-uwu adds a rooms info command, users set-profile-key, and a historical filter on admin query users iter-users, and removes the abandoned MSC4373 EDU-type preference endpoint from the unstable surface.

  • The room directory admin commands accept a room alias and surface it in public-rooms responses ahead of the canonical alias, while publishing an unknown room is refused. Contributed by @x86pup, who also falls URL previews back to twitter: card tags when the og: values are empty, and newline-delimits list-backups output.

  • Thank you @Xerusion for documenting Traefik root-domain delegation in (#529), and @byteflavour for raising the btrfs WAL fallocate disk-usage footgun in (#535), now documented.

  • Compliance status pages for the Complement test families join the documentation, admin query raw flush forces a RocksDB memtable flush, room-scoped policy recovery lands as an admin command, database writes go through atomic batches that watcher notifications take as their single source of truth, the runtime can report tokio scheduler latency histograms, and the pool-thread and cache defaults are relaxed.

  • Admin rebuild-relation-index and rebuild-thread-index move from !admin server to the debug suite; existing invocations need the new path.

Bug Fixes

  • Thank you @lhjt for catching in (#515) that appservice-authenticated client requests inferred presence and activity for the user they act as; the exclusion landed in (#517), with activity context passed through the ping arguments behind it.

  • Reopening the database in-process, which a module reload does, restored the configured backup a second time over everything written since; the restore is now claimed rather than read (cd71003). Separately, the -O loop that sets the restore option ran after the check meant to refuse it, so a database could roll back on every start (8e3e339). Both repaired by @x86pup. Sincere apologies to anyone who restored a 1.8.2 backup more than they meant to.

  • Public read receipts are monotonic again. A re-posted receipt took a fresh stream position and re-sent the EDU to appservices and over federation; the stored position now gates the write. Reported by @lhjt in (#516), who shipped the first fix in (#518). Private read markers are monotonic too, and the sender's own send is marked read without publishing a receipt for it. The deletion sweep is bounded by the encoded room prefix, which had let it cross into a sibling room whose id merely shared a prefix (7190ab8).

  • Soft-failed events are handled correctly in three places. A withheld membership or power-level change is kept out of current state instead of being applied locally, which is the outcome withholding it exists to prevent (cb03606). The rejection marker used to reject every later attempt, so an event withheld over a policy refusal could never return once that refusal lapsed; it now expires on the shared upgrade backoff and reports to the origin as withheld rather than failed (5b1fc2d). Policy-server refusals expire after 24 hours (6651f9e), and a corrupt state-after room is contained rather than failing the whole /sync response (b565d92).

  • Left rooms stay in sync when their cached leave state came from a sibling conduwuit-lineage server, thanks to @x86pup: a lone event object is lifted into a one-element array and anything else read as no cached state. To-device events are handled only for local active or appservice-claimed recipients, so a bridge still receives its own.

  • Non-unix and BSD builds are repaired again, courtesy of @obodnikov: the signals trace import (#526), the in-place restart import (#527), the cfg(unix) gating that the listener refactor dropped (#528), and the sys/limits nix imports and page size (#536). Device major and minor conversion is fixed for the BSD builds alongside.

  • Native OIDC against Matrix Authentication Service completes. MAS's policy allowlist rejects any scope past openid and email, so our unconfigured default of openid email profile failed every authorization. Reported by @utop-top in (#530).

  • Short-id allocation is serialized, so a racing or repeated caller observes the winner's rows instead of minting a second short id for one identity; a repeated event id inside a single batch reached this deterministically (5cff8b8). Room search tokens are purged by shortroomid prefix, in one atomic pass (59d7224, 423fbce).

  • Keyed mutex entries are reaped on every release path. A contender that never became a guard, through cancellation or a failed try_lock, left its entry in the map forever (a2bac8d).

  • Federation retry wakes land uniformly across the backoff interval rather than within a fixed three-second jitter, so a cohort of destinations that failed together stops retrying together (2600024). A stale queue wake is rejected, and the resolver's in-flight deduplication is actually awaited, so concurrent lookups for one destination share a single resolution.

  • @okias reported a broken documentation link in (#522), fixed along with the packaging READMEs, which now use absolute rendered-docs links.

  • @x86pup landed several more fixes: the admin console is skipped when standard input is not a terminal, appservice response-body read failures are logged, and a failure to notify an appservice of an invite returns a generic error. @dasha-uwu repaired get_all_user_mxcs, which left a trailing user-id record unconsumed and panicked debug builds for any user with uploaded media. Elsewhere, admin debug reports per-column errors instead of panicking on an invalid property name, an unchecked float conversion is guarded, a defaulted listen address that fails to bind is skipped rather than taking the whole listener down, and the RocksDB environment is held in one process-global slot so one database's shutdown cannot strand another mid-close.

Release v1.8.2

Choose a tag to compare

@github-actions github-actions released this 17 Jul 15:44
v1.8.2

Tuwunel 1.8.2

July 17, 2026

New Features & Enhancements

  • URL preview media proxying relays link-preview media through the server, now covering og:video and og:audio alongside images, so the third party sees Tuwunel rather than the requesting client. Nothing is stored permanently: preview media becomes a lazy mxc:// reference fetched from source on demand. A url_preview_user_agent option, with a separate url_preview_media_user_agent, lets previews work for sites that block the default agent. Shipped by @az4521 in (#508), closing their own request for video previews (#394). The preview store is rebuilt on CBOR at the same time, replacing a byte-separated format that could shear fields.

  • Distribution packaging expands to RPM with a COPR build pipeline (fixes #251) and a SELinux policy module shipped as a selinux subpackage (#412), plus an apt repository published from CI and Debian packaging that adopts an existing conduwuit or Conduit database in place. Courtesy of @x86pup.

  • Online backups can now be restored and verified, joined by a delete-backups admin command, graciously contributed by @x86pup.

  • MatrixRTC transport discovery (MSC4143) is served without an access token, so Element Call can find a server's transports; contributed by @basnijholt in (#512).

  • A container HEALTHCHECK, backed by a new liveness-probe mode, lets orchestrators track readiness, with appreciation to @x86pup.

  • A dns_servers config option makes the /etc/resolv.conf dependency optional, tip of the hat to @x86pup.

  • The Synapse-compatible admin API grows again (#38): server-notice endpoints, user redaction and login-as, federation destination management, and media info, purge, and statistics.

  • Appservice transaction extensions deliver richer data to appservices: device-list changes and one-time-key counts with unused fallback key types (MSC3202), and to-device events (MSC4203). Opened by @dark-collective in (#502) and (#501).

  • Forward-extremity capping and pruning guards against extremity blowup with a scored prune engine that always leaves a survivor and protects the local server's own leaves, a cap applied on the federation receive path, and admin room commands to list and prune a room's extremities.

  • Local state derivation for incoming federation events lands in observation mode (#419). The server derives an event's state from local ancestry and calls /state_ids only for physically absent events, running alongside the existing fetch and comparing while the fetched result stays authoritative.

  • Long admin command output is split across chained reply or thread events, and oversized output is attached as an uploaded file, raised by @grinapo in (#471).

  • Pushers rejected by the push gateway are now removed, backed by push-gateway conformance tests and UnifiedPush documentation, raised by @NinekoTheCat in (#20).

  • Support for Matrix v1.18 and v1.19 is declared in /versions.

  • The max_fetch_prev_events default is raised to 1024.

Bug Fixes

  • Federation delivery no longer runs hot against a peer that has come back. The per-server backoff gate consulted only the current time bucket, so it re-authorized attempts at every timeout boundary and never honored the computed earliest retry, and a stale set of reachability rows could keep muting a recovered server. The verdict now derives from a server's full failure history, a returning peer clears the whole streak, and the old rows are cleared once on upgrade (da0c3f6, ec049f6). Sincere apologies to anyone whose outbound federation lagged to a server that had recovered.

  • A proxy or CDN answering a federation request with non-JSON is treated as transient rather than evicting the route outright, and route override eviction is fixed for well-known and SRV-delegated topologies where it was a no-op (b33415d).

  • Tuwunel refuses to initialize over the remnants of a database that lacks a readable manifest, instead of treating them as obsolete files and deleting them on open (fixes #510). Reported and diagnosed by @ItsLiyua, whose detail on the two parallel database directories localized the cause.

  • Native OIDC login completes again: the redirect-completion path returned 405 and produced a redirect Chrome refused (fixes #504, #505). Reported by @isniz and @achetronic.

  • One-time-key counts match Synapse's shape, and an explicit zero count is preserved, so a client whose key pool is drained still sees signed_curve25519 and replenishes instead of starving (007033c, 164b8da). Contributed by @basnijholt in (#511).

  • A soft-failed inbound event could compute an empty forward-extremity set and, once persisted, remove every leaf and wedge local sends until a remote event arrived; the previous band is now preserved, and a detached non-create local event on an empty frontier is refused rather than silently forking the room (e0f1034, 1f1dea6).

  • The inbound federation profile query returns 404 M_NOT_FOUND for an unknown user instead of an empty 200 (76ea07f).

  • The room ephemeral section is always present in /sync responses now, thanks to @x86pup (79bb4af).

  • An empty device_id is treated as unspecified and a device id is generated (cfe73cb).

  • A systemd unit no longer sticks in the deactivating state after an in-place admin restart, fixed by @x86pup (68e034d).

  • Non-Linux builds get several repairs, courtesy of @obodnikov: resource-usage reporting compiles on non-unix and no longer panics in macOS thread usage (#509), Ctrl+C actually shuts the server down on non-unix targets (#507), and platform-gated admin commands compile on every target (#506).

  • Backup requests that cannot create a backup error instead of reporting success, and backup engine errors propagate rather than being swallowed (f6de800, 9b54209). Credit to @x86pup.

  • @x86pup corrected documented config defaults that disagreed with the code (bb9dfb2), and the notification_push_path description is set right (e16a3ae).

Release v1.8.1

Choose a tag to compare

@github-actions github-actions released this 10 Jul 04:25
v1.8.1
af3b4ad

Tuwunel 1.8.1

July 9, 2026

New Features & Enhancements

  • Synapse-compatible admin API. The Synapse admin surface is served: user, room, media, device and access-token endpoints, the version and event-fetch endpoints, and room deletion, purge, and background-task tracking, backed by an in-memory task tracker and documented on a coverage status page. Opened by @iwalkalone69 in (#38). The user endpoints include listing a user's joined rooms, opened by @ngophuocloi-miracle-aavn in (#494).

  • Threads list (MSC3856). The /threads endpoint now orders threads by latest activity, honors an include=participated filter, serves per-requester views that respect ignored users, guards its inputs upfront, and carries the newest edit on each thread's latest_event.

  • Stable threading (MSC3440). Threading is advertised in /versions, the related_by_senders and related_by_rel_types event filters are implemented, and nested thread relations are rejected at the send endpoint.

  • Sender erasure (MSC4025). An erasure marker lands with admin surfacing, erased senders' events are served as pruned copies, and federation serving of those events is gated accordingly.

  • Native OIDC account registration and login, so Tuwunel can act as its own identity provider. Requested by @temp1403-oss (#479).

  • Configurable default power-level override for newly created rooms, courtesy of @basnijholt in (#496).

  • User suspension is now enforced at the API boundary, contributed by @dasha-uwu.

  • OAuth falls back to Apple id_token claims when the userinfo endpoint fails, shipped by @basnijholt in (#495).

  • The admin query raw commands gain a put command and hex key decoding, from @dasha-uwu.

  • A SECURITY.md with a detached PGP signature, along with issue and pull-request templates and contact links, graciously added by @x86pup.

  • Event bundling advances across three proposals: aggregations bundled on search context events (MSC3666), m.reference children bundled as an event-id chunk (MSC3267), and the latest m.replace edit bundled as a full event (MSC3925).

  • Private read receipts now carry a timestamp.

  • Rust is bumped to 1.95.0.

Bug Fixes

  • Sliding sync silently dropped m.space rooms, so spaces were absent from the room list where Synapse showed them; the list filters a client omits are now cleared before applying (MSC4186, fixes #503). Reported by @sdenike.

  • Rooms made space-visible did not appear in the space overview for new users. The room hierarchy cache is now evicted on any state change (fixes #498). Reported by @Lazalatin.

  • Registration with OIDC and LDAP configured together was broken: LDAP users are now provisioned even when provider registration is disabled (fixes #499). Reported by @balintbarna.

  • Tuwunel builds on FreeBSD again, with rust-rocksdb vendoring RocksDB there (fixes #492). Reported by @syobocat.

  • The MatrixRTC/Livekit setup docs were missing the Docker address-advertisement configuration, now explained (fixes #493). Reported by @Wanja-L.

  • Conduit database import gains several repairs: the roomuserid_joined repair runs in a single pass, the conduwuit-era membership repairs are skipped for Conduit imports, and systemd's start timeout is extended so long startup migrations are not killed (#41). Thanks to @x86pup.

  • The remote-server version endpoint returns our own version for a self-query instead of failing, with appreciation to @x86pup.

  • Unauthenticated TURN access was possible with turn_allow_guests enabled; guest access is now gated and appservice users are excluded from the guest TURN credentials check, credit to @dasha-uwu.

  • Federation delivery is steadier: a stale resolver route is evicted on a non-JSON response (9bac544), the sender flushes when an unhealthy peer shows inbound activity (2d9c684), a first-failure retry grace precedes the backoff curve (e7f5769), and the sender wakes to retry a failed destination (93a772e).

  • EDU delivery is more reliable: selected device-list and receipt EDUs persist until acknowledged (ffdfc1b), EDU selections queue past the transaction budget (7091f84), and fresh EDUs are selected on the post-response path (3569317).

  • /messages is forbidden on a room the requester cannot see (a791d7e).

  • An unsupported method on a known path returns 405 instead of 404 (cd0513a).

  • The request extractor separates an empty body from a malformed one (80727f8).

  • is_direct is omitted from member events unless it is true (adb78b9).

  • The registration email binds regardless of UIA stage order (9b58cae).

  • The error log on undecodable presence data is restored (d81f340).

  • FIFO cache column TTLs are bounded to each column's validity window, with intra-L0 compaction enabled for those columns (9b3011a, e9004c9).

  • Release builds could fail to compile the room-summary layout after a 1.8.0 change; boxing the membership format at the invite edge cuts the recursion (regression d2c473f).

Release v1.8.0

Choose a tag to compare

@github-actions github-actions released this 27 Jun 04:57

Tuwunel 1.8.0

June 27, 2026

New Features & Enhancements

  • Conduit database migration available again courtesy of @x86pup. Open Tuwunel with an existing Conduit (or foreign-lineage) database directory and it migrates in-place: rooms, original media including S3-backed objects, pending knocks, and suspended or locked users all carry over, media is attributed to its local owners, and conduit-migrated rooms become usable. Raised by @Korvox in (#41).

  • Third-party-identifier (3pid) email support (MSC2290) arrives with outbound email, 3pid storage, the email-3pid request and management endpoints, and admin commands to manage and query email 3pids.

  • Matrix Authentication Service (MAS) support is implemented: the provisioning API endpoints, synced provisioned email bindings, and SSO redirect-action forwarding (MSC3824, so a register action becomes prompt=create on the upstream OIDC request). Opened by @CEbbinghaus in (#266).

  • A remote-server version API, a new client endpoint exposing the versions reported by remote servers, shipped by @dasha-uwu (d038c5b).

  • SSLKEYLOGFILE support for the outbound client, for debugging federation TLS, also from @dasha-uwu (5f00e39).

  • User reports can post to a configurable room instead of the admin room, so operators can moderate without holding server-admin, graciously contributed by @x86pup. Opened by @iwalkalone69 in (#180).

  • The nginx reverse-proxy example collapses its duplicate 443 and 8448 server blocks into one, tip of the hat to @Daniel15 in (#487).

  • Push gateway resolution now runs through the validating resolver, so the private-IP CIDR denylist (ip_range_denylist) applies to pusher delivery, including redirect hops (28e08c2). Operator note: this silently stops delivery to any push gateway on a private or loopback address (a localhost Sygnal, a LAN ntfy or UnifiedPush) until ip_range_denylist is adjusted to permit it.

  • Spec compliance advanced across many endpoints: stable /v1/mutual_rooms with count and paging (MSC2666, cd5004e); rich-text room topics preferring the m.topic block with legacy fallback and indexed for search (MSC3765, fc29fe4); extended profile fields with enforced size and grammar limits (MSC4133, 03b9909); invite and knock stripped state carried as full federation PDUs so a receiver can bind the create event (MSC4311, d2c473f); a registration terms stage (MSC1692, opened by @erebion in #289); appservice device management (MSC4190, opened by @ngophuocloi-miracle-aavn in #488); the user-report endpoint returning 200 for unknown users to deter enumeration (MSC4277, 6ddd590); per-requester thread bundles with current_user_participated and the full latest_event (MSC3816, 3c3e65a); search results populating surrounding event context and pagination tokens (945e79c); device-list updates flushed to federation on key change (eff8e52); and federation get_missing_events serving stored canonical JSON so unmodeled fields survive (e2eca44).

Bug Fixes

  • The local server is now always exempt from allowed_remote_server_names and forbidden_remote_server_names. A 1.7.x allow-list that omitted the local name classified a local user's own events as coming from a forbidden remote and dropped them (fixes #489). Reported by @BurningEnlightenment. Sincere apologies to anyone whose own users went quiet.

  • /timestamp_to_event (MSC3030) returned M_NOT_FOUND for valid searches because the room-scoped scan stopped on the first foreign-room key; fixed by @lingbohome in (#477).

  • make-user-admin now grants the correct power level when the target is already in the admin room, with appreciation to @x86pup. Reported by @mio-19 in (#84).

  • LDAP is skipped during UIA reauth for non-LDAP accounts, so resetting device keys on a non-LDAP account no longer triggers a filterless LDAP search, credit to @x86pup. Reported by @kuhnchris in (#255).

  • A lost-wakeup in until_shutdown that could stall a service worker is fixed (22676a3).

  • /timestamp_to_event falls back to federation for local misses and forward start-edge queries (MSC3030, 914b16c).

  • Several federation membership fixes: a re-invite over a stale local ban is honored, an out-of-band invite rescission is applied, join_authorised_via_users_server is ignored for an existing member, custom /join body keys are merged into the member event, and kicking a non-member returns 403 (a37bd24, b5101ac, 836831c, 8e135be, fae1595).

  • Lazy-loaded incremental sync no longer drops changed members other than the syncing user (MSC4222, 0e5800c).

  • State resolution for pre-v12 rooms now begins iterative auth checks from an empty initial state, matching the handling used for version 12 rooms (954b0c3).

  • Per-room push rules now carry across a room upgrade (c852ae2).

  • A device's local notification settings are removed on every device-deletion path (MSC3890, e0283e0).

  • The single-event endpoint returns 404 rather than 403 for an event hidden from the requester (404b516).

  • A client invite to a server lacking the room version returns M_UNSUPPORTED_ROOM_VERSION (MSC1866, 5869123).

  • Version 12 room upgrades omit the deprecated predecessor.event_id (MSC4291, 6625065).

  • The /context end token is positioned so a backward page still includes the base event (f3fe502).

  • The [global.smtp] config section is fixed (6061c5c).

  • Notable for operators: the RocksDB storage engine is bumped to 11.1.1 (ab32765), and the first 1.8.0 boot on an existing database performs a one-time timestamp-index rebuild from pduid_pdu, which lengthens first startup on large instances (41f0de0, 873f670).

Release v1.7.1

Choose a tag to compare

@github-actions github-actions released this 05 Jun 17:34

Tuwunel 1.7.1

June 5, 2026

New Features & Enhancements

  • A new federation data-fetching service improves reliability in rooms whose history is spread across many servers by locating missing events through concurrent queries. It ranks candidate servers by room-membership popularity and recent reachability, and reuses requests already in flight instead of issuing duplicates. Resolved state for outlier events, fetch and backoff decisions, and per-server reachability ("Peer Status") are now cached persistently, and auth-chain, state, prev-event, and backfill fetches all run through it. Servers that used to re-request the same uncacheable lookups should see far less repeated federation traffic.

  • OIDC device authorization grant (RFC 8628) lets users sign in on input-constrained devices. The MSC4191 account-management action set is now complete with a deep-linked cross-signing reset, and MSC3861 OAuth 2.0/OIDC is advertised on /versions. The token endpoints and refresh-token lifecycle were reworked, dynamic client registration is opt-in and validates submitted client metadata, and device-scope binding requires PKCE. The OIDC authorization-server chapter of the documentation was expanded to match.

  • Several additional MSCs land this cycle: MSC3980 (event_fields trimming on /sync), MSC3860 (media download redirects), MSC4311 (origin_server_ts on the stripped create event), MSC1219 (key backup storage conformance), MSC2659 (appservice ping error codes), MSC3550 (403 M_FORBIDDEN allowed on profile lookup), and a stable m.forget_forced_upon_leave capability (MSC4267). MSC4380 invite blocking now also covers invites delivered through /sync and createRoom.

  • Support-contact discovery gains a PGP field and policy links (MSC4439, MSC4266), graciously contributed by @x86pup. The /.well-known/matrix/support endpoint can now advertise a pgp_key per contact (with raw key material rejected) and link support policies, and multiple support contacts can be configured with validation.

  • @dasha-uwu added an admin media preview command for debugging URL previews, retired blurhashing, dropped the legacy media-preview redirect, and removed the deprecated server-keys endpoint.

  • Sliding sync (v5) now retracts departed and left rooms from the list and adds re-invited rooms back, so clients track membership churn without a full resync.

  • A device may now hold multiple access tokens, for easier rotation and concurrent sessions.

  • /context can optionally resolve events it has not yet received over federation, and outbound HTTP compression gained per-direction opt-out switches.

  • An admin command to purge every room containing a given user was added, raised by @winyadepla in (#472).

  • Documentation for ip_source_trusted_subnets now warns about accidentally including a proxy in the trusted set, courtesy of @BVollmerhaus in (#468).

  • Diagnostic admin command suites were added for the federation fetcher and Peer Status, and the runtime can dump tokio and getrusage metrics to JSON at exit.

Bug Fixes

  • A regression introduced with ip_source in 1.6.1 blocked locally-connected appservices and other loopback clients (#465). Loopback peers and trusted-peer subnets now bypass the configured ip_source, including over the Unix-socket listener, and the axum-client-ip dependency was replaced with inlined helpers. Sincere apologies to everyone whose bridges went quiet.

  • Remote room directory and summary lookups are more resilient over federation: the room-summary fallback now tries every via server (5c99983), and a failed remote publicRooms request now returns a 502 (9a87977).

  • Thank you @x86pup for reporting in (#466) that a bad unix_socket_path produced an opaque startup failure; listener initialization errors now name the offending path.

  • !admin query oauth associate replied with an empty message and did nothing, reported by @Vazgen005 in (#467). It now emits a confirmation and accepts a force flag.

  • @dasha-uwu fixed a compression configuration option that could accidentally disable client-side decompression.

  • Several federation correctness fixes: the federation lock is now held across the invite residency check to close a join/unban race (add512b); a send_join response that omits state fails over to other servers (9c158d3); each transaction's PDUs are sorted topologically before handling (91218e1); and references outside the auth graph are treated as non-edges during resolution (6643919).

  • Knock membership is now persisted and a remote re-knock re-drives to reconcile state; per-PDU backfill errors are isolated so one bad event no longer aborts the batch; and thread redaction walks through the redacted target.

  • Media fetches and URL previews now honor CIDR denylists for the addresses they resolve to (af1266a, 554557c). Buffered outbound responses are size-bounded, and federation key lookups are bounded and backed off.

  • Configuration handling improved: an unreadable client_secret_file now reports the path and IO error (844f123), matched keys can be excluded from the "unknown to tuwunel" warning (6bbfd0a), and packaged builds no longer drop their malloc_conf tuning (de0eb1d).

Release v1.7.0

Choose a tag to compare

@github-actions github-actions released this 21 May 13:19

Tuwunel 1.7.0

May 21, 2026

All servers raising their cache_capacity_modifier above default must consider decreasing it to deploy this release. The default of 1.0 is now generally recommended, and up to 2.0 for systems with four or less cores. Taking no action may cost 25% to 50% more memory (#123).

Nine additional MSCs have landed. Current status is kept up to date in our documentation.

New Features & Enhancements

  • Threaded read receipts and notifications (MSC3771, MSC3773) ship across storage, federation, sync v3, SSS v5, and the push gateway. Existing users may see a one-time jump in unread or badge counts that previously sat at the main-context-only total.

  • MSC4225 one-time-key upload-order issuance is implemented. Operator note: the legacy onetimekeyid_onetimekeys column is dropped on first read-write open and existing OTKs are erased; clients re-upload on their next /sync, with MSC2732 fallback keys (where present) covering the gap. The wipe is one-way; read-only and secondary replicas tolerate the missing column until the primary recreates it.

  • MSC4222 state_after on /sync v3 as an opt-in via ?use_state_after=true. Clients that don't opt in are unaffected.

  • MSC4115 unsigned.membership on served events from encrypted rooms. Informational; clients that ignore the field are unaffected.

  • Synapse-compatible shared-secret register endpoint at /_synapse/admin/v1/register, raised by @iwalkalone69 in (#38). The admin module was also split into a directory of units.

  • Refresh-token expiry with optional hard logout via three new keys: refresh_token_ttl, refresh_token_idle_only, and refresh_token_hard_logout. All default disabled.

  • Configurable extra OIDC authorization parameters, courtesy of @Batmaev in (#450). Closes the silent-relogin gap with Logto and Element X when operators set prompt = "login". Thank you!

  • MSC4452 preview_url capability is advertised on /capabilities.

  • MSC4466 propagate_to profile query parameter is implemented; the room fan-out also runs concurrently.

  • MSC3283 m.set_displayname and m.set_avatar_url capabilities are advertised.

  • MSC3814 fallback keys on dehydrated-device PUT are now stored.

  • OpenTelemetry OTLP exporter replaces the deprecated opentelemetry-jaeger crate, closing (#85); tokio-console is re-enabled.

  • Tip of the hat to @nyakokitsu, who reported in (#460) that turn_uris set without TURN credentials produces empty creds silently. Tuwunel now warns at startup.

  • Thanks to @dasha-uwu for simplifying servers_route_via.

  • Per-cache defaults were rebalanced from observed utilization in (#123): auth_chain up 15x to 1.5M, several shorteventid/eventid caches 4x to 400k. Closes (#123) and (#423) opened by @scanash00; an earlier report by @alythemonk in (#262) on Oracle Linux OOM-via-PGTABLE is also addressed. Operators on <= 2 GiB hosts may want to clamp the cache modifiers in their toml to restore the previous baseline.

  • Admin server memory-usage is now tabular (used / cap / util%) with per-pool block-cache rows.

  • Configuration items are annotated in the generated tuwunel-example.toml to indicate runtime-reloadable vs restart-required.

  • The KV codec's additive-tail invariant for trailing Option<_>, &str, and &[u8] fields is documented.

  • A migration upgrades legacy mediaid_user keys to the composite layout.

  • Thank you @NiklausHofer for the Gentoo Guru ebuild in "Getting Started" (#461).

  • Docs: enable_loopback_candidate troubleshooting motivated by @Lama-Thematique in (#451), ip_source for reverse-proxy users, and an MSC table refresh (MSC3419 verified, MSC1957 n/a, rows for MSC4461 through MSC4474). Closes (#94) opened by @MrRinkana.

  • Thanks to @winyadepla for the traefik MatrixRTC config in (#462) bringing parity with nginx and caddy. Also addresses (#69) opened by @GZEI.

  • CI gained a Complement job-summary script, a Playwright stage, and drift detection so a missed tuwunel-example.toml regeneration fails check and clippy.

Bug Fixes

  • A v1.6.2 regression on non-S3 storage backends panicked the main thread on first upload. Reported by @Sommerwiesel in (#452); multipart is now gated on S3 only. Sincere apologies for the disruption.

  • @BVollmerhaus graciously reported in (#454) that MSC2246 asynchronous media uploads could self-deadlock on the notifier mutex; the acquisition order is fixed. Thank you!

  • Thanks to @digikar99, who reported in (#459) that the UIAA fallback acknowledgement rejected non-SSO flows; the registration token + password flow completes cleanly.

  • Sliding-sync cached list ranges refresh on every explicit list update, shipped by @lhjt in (#455); previously a scrolled-to range was ignored indefinitely. Thank you!

  • /threads and /backfill apply the visibility filter before pagination take, so a final non-empty page still returns next_batch.

  • GitHub OIDC default base_path now aligns with their published discovery doc after they quietly changed the issuer (eb51c70, 6552f86).

  • OAuth Dynamic Client Registration records are bounded to a fixed size. Thanks @CEbbinghaus! (e5f625d).

  • OAuth SSO grant params win over operator-configured extras on key collision (05dba7e).

  • Receipts and presence EDU emission is bounded below the federation budget (b4fcf58).

  • Membership tolerates stale room state on self-leave (39c72c2).

  • A missed optimization in /state and map-value storage was corrected (b305e6a); /state now also propagates per-PDU read errors instead of silently skipping corrupt events.

  • Five route doc-headers had incorrect HTTP verbs (a40ca8f).

  • The Docker bake file dropped the non-functional cache_to/cache_from directives (8e4bc8c).

  • Thank you @pedrompcaetano for the typo fix in tuwunel.container (#456).

  • Stale comments and fan-out destructure cleanups (85e85c8, 9c4cd7c, 2cc2493).

Release v1.6.2

Choose a tag to compare

@github-actions github-actions released this 08 May 18:38

Tuwunel 1.6.2

May 8, 2026

We have started a specification compliance campaign which will continue over the next several releases until synced with 1.18 (or 1.19 if it takes that long). Current status will be kept up to date in our documentation.

New Features & Enhancements

  • Policy server support (MSC4284) ships; two new config keys: enable_policy_servers and policy_server_request_timeout.

  • Account locking and suspension (MSC3939, MSC4323, and MSC3823), plus an admin command to mass-reject pending invites.

  • MSC2732 Olm fallback keys are implemented and re-issued on each subsequent claim, so clients keep receiving after key exhaustion.

  • MSC4380 invite blocking (partial).

  • MSC4406 sender_ignored on single-event endpoints.

  • MSC4383 /versions discovery.

  • MSC4260 user reports.

  • MSC4373 incoming-EDU types over federation.

  • MSC4168 m.space.* state copied on upgrade.

  • MSC4169 backwards-compatible redactions on send.

  • MSC3905 local-only users namespace matching for appservices.

  • MSC4025 partial erase on /deactivate.

  • MSC3391 account-data deletion.

  • MSC4361 non-federating member auth rules.

  • MSC4190 appservices now skip UIA on cross-signing key replacement.

  • MSC4254 OIDC revoke handler is tightened across request shape, error codes, and provider lookup.

  • MSC4175 Timezone-key routes have been updated to the stabilized form.

  • Thanks to @DBendit who opened (#316): a complete list of MSCs Tuwunel supports is now in the docs.

  • @dasha-uwu shipped cleanups: appservice file filter, conditional admin lookup, two-member room naming, thumbnail logging, remote media ids.

Bug Fixes

  • Sliding-sync bump_stamp is graciously fixed by @lhjt in (#449), so Element X and other clients move rooms in the sidebar on new activity.

  • Thanks to @humemm for (#448), where OAuth responses returning expires_at as a Unix timestamp tripped login; the upstream DTO is now decoupled.

  • Tip of the hat to @maxrdz for the NGINX root-domain delegation example in (#446), with default port and a resilient $backend indirection.

  • State resolution corrections: knock auth v7-9 (aea509f), auth-difference (631c51a), mainline 0 (82132ee), v12 bootstrap-join (aaa6a1a). A few were upstreamed to Ruma.

  • A long-standing /sync concurrency heisenbug (b1ac65b), originally introduced in Conduit and made slightly worse by optimizations which took place in v1.3.0, has finally been zapped.

Release v1.6.1

Choose a tag to compare

@github-actions github-actions released this 01 May 14:37

Tuwunel 1.6.1

May 1, 2026

New Features & Enhancements

  • Next-gen OIDC account management, courtesy of @shaba in (#407), implements MSC2965 and provides the in-browser session list, session-end flows, and profile page for users authenticated via OIDC. The same PR fixes URL-encoding of idp_id in the SSO redirect path and adds the SSO/OIDC bypass path through User-Interactive Authentication so that users without a password can complete UIAA-protected actions. This closes (#433) opened by @jonathanmajh. Thank you!

  • Appservices with receive_ephemeral now receive EDUs scoped to their namespaces in (#406), shipped by @chbgdn and closing (#382). m.typing and m.receipt now route to subscribed bridges and bots. Confirmation testing was provided by @gymnae, thank you both!

  • systemd watchdog keep-alive pings were graciously added by @VlaDexa in (#415). Unit files declare WatchdogSec=30 and the runtime pings systemd, so an unresponsive process is restarted automatically; previously-tolerated long stalls (e.g., pathological state-resolution) may now trigger restarts.

  • Spoofing-resistant client-IP resolution with a configurable ip_source was contributed by @theredspoon as a security finding (#427), implemented and landed across (#428) and (#429). The new ConfiguredIpSource extension and ClientIp extractor replace axum_client_ip::InsecureClientIp across the API, restoring trust in client IPs for rate-limiting and audit logging. Default behavior is unchanged for existing deployments; operators behind a trusted proxy should set ip_source to opt in.

  • MSC3030 (/timestamp_to_event) is implemented (experimental), contributed by @donjuanplatinum in (#413). Clients can now jump to a specific point in time within a room. This is the third Matrix Spec Change @donjuanplatinum has shipped to Tuwunel and we are very grateful for the consistent contributions.

  • MSC3824 (delegated authentication / refresh-token capability) is advertised on /versions and LoginType::Sso includes delegated_oidc_compatibility. The config key sso_aware_preferred is renamed to oidc_aware_preferred, with the old name accepted as alias.

  • Thanks to @rexbron, who contributed extensive operational documentation in (#354) and (#438): a testmatrix example in the troubleshooting section, podman-quadlet examples, an OIDC Keycloak provider example, refactored troubleshooting links, and clarification of how to obtain provider_id for the user admin commands. Thorough work!

  • @valentimarco wrote a complete Authelia authentication page in (#278), closing their own (#274) on the OIDC token endpoint. Thank you!

  • Thanks to @winyadepla for reorganizing the calling chapter in (#431), clarifying TURN vs MatrixRTC and the rationale for Docker-only deployment. This addresses (#348) opened by @MadMan247. Thank you both!

  • Thank you @alametti for adding an Authentik provider section in (#437).

  • Configuration values that name byte sizes now accept SI/IEC unit strings (64MiB, 2GB, etc.) in addition to raw integers.

  • A persistent LRU cache was added for userdevicesessionid_uiaainfo to keep ongoing UIA sessions alive across restarts.

  • Performance: appservice EDU conditions reworked for concurrent lazy serialization; lazy-loading witness write-back gained a mode argument; the legacy spacehierarchy runtime cache was replaced by a database-backed path (config key roomid_spacehierarchy_cache_capacity β†’ spacehierarchy_cache_ttl_min/spacehierarchy_cache_ttl_max).

  • Admin: new commands to dump PDUs to the filesystem, query the RocksDB sequence number, and force/override or bypass database migrations.

  • Bootstrap stamps a server_name marker into the global column family (backfilled on first boot for pre-existing databases) so a misconfigured server_name pointed at the wrong database is caught on every start.

  • The media_storage_providers config option now validates that named providers exist; an explicit empty provider list defaults to all configured providers.

  • New documentation chapters: Authentication Systems overview, JWT auth, LDAP auth, multimedia and storage, storage-provider environment variables. Identity-linking semantics for trusted vs. untrusted IdPs are now documented. The development chapter links hosted rustdocs (newly deployed via CI) and a Testing section was added. (#324) opened by @TheButlah on the NixOS Module documentation is closed.

  • OCI image labels now include accurate org.opencontainers.image.version and related metadata derived from the package, closing (#356) opened by @rexbron. Thank you for the detailed write-up!

Bug Fixes

  • OIDC server-contract hardening: /_tuwunel/oidc/userinfo rejects plain Matrix access tokens (with WWW-Authenticate: Bearer on 401); the token endpoint returns 400/invalid_grant instead of 500 on client errors and emits Cache-Control: no-store; PKCE plain is no longer accepted (only S256); the m.oauth UIA flow routes through /login/sso/redirect when no specific IdP is selected.

  • Storage-provider variant naming is now consistent, with appreciation to @yonzilch for (#414). Both sub-tables use lowercase identifiers ([global.storage_provider.<ID>.s3]), unblocking environment-variable configuration. Existing S3 configurations are still accepted.

  • OpenBSD startup is fixed in (#422), tip of the hat to @Hukadan. core_affinity_rs misreports CPU counts on OpenBSD; Tuwunel now uses num_cpus there. Thank you for picking this up!

  • @alaviss reported a 1.6.0 regression in (#432) where inline [global.appservice.<ID>] config no longer worked. Fixed in (9d10230); the appservice ID from the toml section is honored again. Sincere apologies for the inconvenience.

  • Multiple users reported the room-spaces hierarchy endpoint returning incomplete or invalid results: @vrisalab in (#344) and @foxing-quietly in (#399). The hierarchy unit was refactored, optimized, and corrected (including discarding m.space.child events with empty content per MSC1772/MSC2946). Special thanks to @TheBrigandier for testing and confirming the fixes on both threads.

  • Thanks to @utop-top, who reported in (#411) that S3 uploads to Cloudflare R2 timed out for large media (~200 MiB+). Multipart uploads now kick in above a configurable multipart_threshold (default 100 MiB). We appreciate the patient testing!

  • Thank you @utop-top for also reporting in (#401) that appservice E2EE was broken because /whoami wasn't returning a device_id per MSC3202, crashing matrix-hookshot on startup. Tuwunel now accepts and asserts the appservice-supplied device_id per MSC4326. Confirmation testing was provided by @Domoel, thanks to you both!

  • @BVollmerhaus reported in (#327) that mautrix bridges (e.g., mautrix-signal) couldn't upload device keys via MSC4190, blocking Element's upcoming mandatory device verification rollout. The MSC4190 path no longer stores as_token as the access token, and honors the appservice-asserted device_id on create. Special thanks to @1matin, @Domoel, and @gymnae for active testing across the thread.

  • Sliding-sync long-polls now release on client disconnect, credit to @chocycat for (#386). Refreshing a client no longer leaves the previous poll holding the connection mutex for the full timeout. Supplemented by task-detach and shutdown-timeout abstractions on main.

  • Thanks to @kodazavr for the immediate report in (#444): Tuwunel failed to start with Sentry integration enabled because the Sentry transport was missing a TLS backend. The reqwest transport is now built with merged webpki roots.

  • Thank you @dennisoderwald for catching in (#443) that OIDC discovery advertised response_mode=fragment while the authorize endpoint only accepted query. Both modes are now implemented through the authorize/complete path.

  • @dennisoderwald also reported in (#434) that S3 storage worked over HTTP but not HTTPS. The missing tls-webpki-roots feature was added to the object_store dependency. Confirmation testing was provided by @ZoftTy and @kodazavr, thank you all!

  • Thanks to @dlford for the report in (#403) that clearing the presence status message had no effect. Now implemented with correct state transitions.

  • Thank you @oly-nittka for the careful diagnosis in (#385) that federation with matrix.org was failing: a stale SRV cache entry overrode the .well-known delegation, producing port 8443 instead of 443. The actual_dest_2/actual_dest_3_2 resolver paths now parse explicit ports from delegated hosts.

  • @native4don reported in (#377) that device_lists.changed was missing from /sync after cross-signing key uploads or device-key changes. The per-room device-key-change row was restored and the sync path updated. Confirmation testing was provided by @rexbron and @x86pup, thank you all!

  • Thanks to @Giwayume for spotting in (#376) that GET /_matrix/client/v3/devices returned null for display_name and was not spec-compliant. The Ruma Device type now skips serializing optional fields when absent.

  • Thank you @foxing-quietly for the report in (#372) that GET /room_keys/version returned 500 instead of 404 for stored backups predating the algorithm field. A backup-algorithm serializer now migrates legacy records on the fly.

  • @kuhnchris reported in (#435) that AppService regex matching was case-inconsistent. User and alias namespaces now use case-insensitive comparisons, matching how MXIDs are normalized. Thank you!

  • @Himura2la updated the LiveKit configuration documentation to the modern form in (#420), addressing (#400) reported by @Morgan-SL-PUP. Thank you both!

  • Thanks to @grinapo for noting in (#317) that the Caddy .well-known example used incorrect CORS syntax. The example was corrected.

  • Thank you @jameskimmel for correcting the Docker port mapping in the example to match the listener (#393).

  • A regression in state_cache where the per-user transit step wa...

Read more

Release v1.6.0

Choose a tag to compare

@github-actions github-actions released this 12 Apr 07:46

Tuwunel 1.6.0

April 9, 2026

New Features & Enhancements

  • Next-Gen Auth OIDC server enhancing ElementX and SchildiNext has arrived! It all began only a month ago with (#342), a large draft PR by @lytedev assessed by the Tuwunel team to be several months away. What happened next was truly extraordinary. Starting with @chbgdn and followed by @siennathesane, @DonPrus and @shaba an entire project within this project assembled to test and iterate this branch at a rapid clip. The OIDC server now builds on existing infrastructure in Tuwunel previously used for SSO. If you have an Identity Provider configured already for use with SSO then the OIDC server Just Works. Huge thanks to everyone involved. (Implements MSC2964/2965/2966/2967)

  • S3 Storage support is now available! Starting from (#362) graciously developed by @exodrifter, Tuwunel now introduces multiple media backends with configurable sections. Support currently includes S3 endpoints and local filesystem directories. The existing media directory is now itself a configurable storage provider implied by the section [global.storage_provider.media.local]. See the examples under [global.storage_provider.<ID>.S3] to configure your own S3 provider. Then list it in media_storage_providers to download media from it, and store_media_on_providers for uploading media to it. Experimental migration support is available with the !admin query storage sync command. SPECIAL UPDATE: Thanks to testing by @utop-top large uploads (~200 MiB) may not work for some S3 providers until additional support is added in 1.6.1. We apologize for this limitation.

  • User-Interactive Authentication for SSO accounts (MSC2454) has been made possible thanks to @chbgdn in (#389). Accounts no longer require setting a password to use features protected by UIAA flows. Users wishing to disable password authentication on their account altogether may do so by changing it to a single asterisk '*' character (use the admin room commands if your client refuses this password change).

  • User-Interactive Authentication for Next-gen OIDC (MSC4312) was implemented by serial auth-system contributor @chbgdn in (#405). This provides cross-signing/identity reset functionality for ElementX and co.

  • Asynchronous media uploads for appservices was implemented thanks to @donjuanplatinum (MSC2246) in (#347).

  • Thanks to @dasha-uwu the appservice_dir can be configured to a directory containing all your appservice yaml files.

  • @donjuanplatinum implemented the server-side for fast-joins (MSC3706) in (#349). Thank you!

  • Thanks to @ventureoo we support sockets managed by systemd after (#360) (issue #355).

  • @VlaDexa prevented duplicate reactions from being sent by a client to maintain spec compliance with (#353), thank you!

  • Thank you @alametti for adding delegation examples (e.g. example.com to matrix.example.com) to the documentation in (#352).

  • Thanks to @Lama-Thematique the admin room user registration notice was improved in (#387).

  • Thank you @dasha-uwu for implementing the MSC4143 endpoint.

  • Thank you @dasha-uwu for removing the report score per MSC4277.

  • Thank you @dasha-uwu for removing v1 send_join/leave as per MSC4376.

  • RocksDB compaction details are logged for the curious in verbose logging builds.

  • Numerous performance optimizations including JSON deserialization and allocator optimizations.

  • Sliding-sync no longer persists subscriptions across requests.

  • Configuration option allowed_remote_server_names_experimental added as exclusive federation allow-listing. NOTE: the _experimental suffix was added to indicate the logic of this feature will change in an upcoming release and the suffix will be removed. We sincerely regret this inconvenience.

Bug Fixes

  • Thank you @jameskimmel for fixing the nginx configuration for http/2 support. (#391)

  • @exodrifter fixed various errors and typos in documentation (#343), some reported by @RhenCloud in (#338). Thank you both!

  • @VlaDexa fixed systemd reloading by sending monotonic time after consultation with @rexbron. (#359) Thank you both!

  • Thanks to @exodrifter the media delete range commands now have improved verbiage as of (#375).

  • @yefimg fixed the UIA password flow not being advertised to LDAP users due to regression (#378). Special thanks for this!

  • Thank you @proximalriver for fixing the missing server keyword in the nginx example. (#383)

  • @chbgdn fixed the m.change_password capability not being set based on login_with_password. (#388) Thank you!

  • Thank you @centromere for reporting cross-platform build regressions in #357 which were fixed.

  • Thank you @Ada-lave for reporting a regression with admin startup commands in #320 which we fixed.

  • @0x1af2aec8f957 reported the new systemd-friendly listener system required reuse-address flags to be set (#374). Thank you for reporting!

  • Thank you @Batmaev for reporting non-compliant minimum timeout was imposed on sliding-sync in (#402) which was corrected.

  • @dasha-uwu fixed admin room upgrade to work as expected. @dfuchss inspired with (#361) among many other informal reports. We appreciate the effort of everyone involved on this!

  • @tycrek reported the conduit user is involved in force-join-all-local-users commands (#373) which was fixed thanks to @dasha-uwu.

  • Thanks to @dasha-uwu bugs and compliance regarding initial_state during room creation were addressed.