Tuwunel 1.9.4
October 10, 2026
New Features & Enhancements
-
Google Cloud Storage is now a supported media provider, graciously contributed by @asafarian in (#644). On GCE or GKE, metadata-server credentials replace the exported key the S3 interoperability route needed. Set
bucket(prefix inbase_path, not ags://URL) under[global.storage_provider.<ID>.gcs]and list that ID inmedia_storage_providersunless it ismedia. The startup check needsstorage.objects.list, aborts startup on failure and proves no write or delete rights; signing without a private key also needs the IAM API andiam.serviceAccounts.signBlob. -
Native OIDC adds provider choice, thanks to @tototomate123 (#615) and @utop-top's request (#570). Authorization, account and device-login pages offer local passwords or configured SSO with responsive, light/dark cards; @utop-top reported in (#639) that Element X's Manage Account skipped straight to SSO. Device approval stays bound to the signing-in provider. Requires
oidc_native_auth = true(defaultfalse) andwell_known.client. Account management excludes registration; registration policy still applies (da50af4, 2607b7c). -
Servers gain profile-read, display-name and room-alias controls courtesy of @arnolicious (#608), (#609), (#610). Reloadable
limit_profile_requests_to_users_who_share_rooms(defaultfalse) requiresrequire_auth_for_profile_requests = true; unrelated users get 403. Reloadableenable_set_displaynameandallow_room_alias_creationdefaulttrue; documented admin/appservice exemptions remain. @arnolicious's (#607) is partial: avatar controls and federation profile reads remain unchanged. -
Password limits arrive thanks to @obodnikov (#597). Reloadable
global.rate_limiting.login.accountcounts verified sign-ins (default burst five, refill 0.003 tokens per second);global.rate_limiting.login.failedcounts wrong passwords (default burst three, refill 0.17 tokens per second). Zero rate/burst disables each. Password login and native sign-in share both; UIAA uses the failed-password limit only. Exhaustion returns 429M_LIMIT_EXCEEDED. LDAP/SSO/JWT/token login remain excluded. -
Thanks to @x86pup for refreshing dependencies and advancing OpenTelemetry to 0.33 and tracing-opentelemetry to 0.34 (e9eca62, 659bf3e); yoke-derive also advances to 0.8.4 (bdcba0b).
-
!admin users redact-recent <user> <room> <count>redacts a local user's newest messages in a room as that user; the count covers unredacted plaintext and encrypted messages, not rows scanned. Partial failure reports progress without rollback (653ac3e). -
Inspect predecessor fetching with
!admin debug prev-walk-metrics,backoff-metrics,prev-walk-roomsand!admin federation incoming-federation: fetch/upgrade timing, in-flight work, room history and locked rooms without walks. Stock builds warn on capped, failed or cancelled endings; admin diagnostics retain successful walks (f64de3d, 82aa52a, 91358e2). -
Presence scans seek the requested counter window instead of the entire column. Federation encoding skips avatar/display-name loading; presence payloads gain a dedicated cache (a196906, 07761c5).
-
Database helpers gain indexed column handles and four-byte integer decoding. RocksDB column options follow table-factory installation; capacity-derived caches round up to MiB. Identical state hashes skip snapshot loading (6ed002c, d81fe55, 54fccbb, 8f12fad, bdbb049).
-
Unset
servername_status_cache_capacitynow defaults to 200,000 plus 15,000 per available CPU (was 100,000 plus 10,000), counting entries, not bytes; no memory or speed effect is measured (a1e5cd2).
Bug Fixes
-
Thread redactions update counts and latest replies, thanks to @basnijholt (#617), (#618), (#619). MindRoom Chat repeatedly fetched 13 pages for roughly 1,000 events. Root redactions preserve bundles; root receipts leave replies unread. One-time startup reconciliation has unknown duration, depending on stored roots/relations. Backfilled counts remain where replies are absent. Failed roots log errors yet completion is recorded. Latest-reply replacement scans at most
thread_latest_reply_search_limitrelations (default 1024, reloadable); a miss or failed read drops the stale preview and omits that summary from served bundles. The startup pass ignores the limit (20feec8, 281becc). -
Startup repairs short-id residue behind (#586)'s refusal. Thank you @dogarrowtype and @mav96 for the refusal reports. Repair reconciles identities, reconstructs orphan states, cleans proven purge residue, preserves uncertain data and verifies more. Fresh databases skip scans; settled ones check identities once. Attempts lacking recognized durable outcomes can retry (0341e0f, a47f170, 3d2abb9).
-
Federated key queries and claims keep only the answering server's users, shipped by @basnijholt in (#624); another server can no longer overwrite device, cross-signing or one-time keys of local or third-server users. Stored keys are not revisited (113a83d).
-
Thanks to @basnijholt, knocking stops opening rooms early: Sliding Sync withholds invited and knocked rooms' timelines (#625), knock-response member events stay out of room state (#632), and a pending knock no longer grants federation read access (#634). Knock bump stamps come from the membership event (607b104).
-
Deactivating the last active local admin refuses before removing devices or clearing the password, courtesy of @obodnikov in (#604). Users must appoint another admin; passwordless or deactivated admin-room members cannot qualify.
-
JWT login and interactive authentication reject deactivated accounts, thanks to @obodnikov for (#611). Locked-account refusals cover refresh and affected OIDC sign-in/approval/token paths. Deactivation returns 403
M_USER_DEACTIVATED; locks return 401M_USER_LOCKEDwith soft logout. Appservice passwordless-puppet exceptions remain (eb655bc, e85f4dc, 45800bd). -
@basnijholt made email password resets refuse deactivated accounts at the final step in (#628), rather than setting a fresh password. Password writes, creation and deactivation share a per-user lock (7f8b85e).
-
Removing/emptying
emergency_passwordclears server-user sessions and password next startup, as documented. Credit to @obodnikov for (#612). Cleanup includes LDAP, clearing the password last for interruption recovery. Admin user APIs refuse server-user access changes but allow profile edits (fa3eefe). -
JWT
B64HMACworks, graciously fixed by @obodnikov in (#613); legacyHMACB64remains accepted case-insensitively. Enabled configurations reject unknown formats at startup/reload, before login. Accepted:HMAC,B64HMAC,HMACB64,ECDSA,EDDSA. Validation checks format names, not keys (95f9733). -
Admin-banned rooms refuse non-admin member-state joins, knocks and invites, contributed by @basnijholt in (#627). Joins recheck the ban under the join locks, and leaving a banned local room appends a real leave event (f40de15).
-
Tip of the hat to @basnijholt for (#636):
ip_range_denylistnow matches IPv4-mapped IPv6, so[::ffff:10.1.2.3]fails10.0.0.0/8, and the default adds0.0.0.0/8and::/128; explicit lists are not augmented. Literal federation destinations get the same check (b8234c9). -
With thanks to @basnijholt, federation rejects create events contradicting their room, including room version 12's derived ID (73e24ae), and stored predecessor events from another room, walking predecessors at the room's first timestamp too (3245200, fd13f97). Loaded predecessor state must match the incoming room, and
send_joinauth-chain events pass the same room check before storage, skipping invalid ones (94198c3, d3db90b). -
@az4521 taught URL previews to fetch media from the page's final redirected URL, rechecked against host policy, in (#623), fixing kkinstagram.com and kkclip.com embeds whose crawler redirect reaches an Instagram CDN
.mp4while other agents get a landing page. -
Thank you @az4521 for reporting missing-
room_idin (#616). Adminforce-set-room-state-from-serverhandles room-version-12 create events omitting it; state, auth-chain and incoming knocks normalize before storage (0428ba7, 0a707a3, 1c6dd68). -
Mistyped
!admin users reset-passwordreports absent local accounts instead of creating state. Thanks to @morteng for (#599). Other user-writing commands/admin grants reject absent accounts.!admin users revoke-adminclears accountless staged grants but refuses room-command self-revocation and server-user revocation (10fc18f, 0a6eef5, 3492880). -
Credit to @basnijholt for (#635): failed appservice requests no longer log the request URL carrying the
hs_token; the appservice and its registered URL are still named. -
Pending interactive-authentication sessions retain at most 1,024 request bodies of up to 4,096 bytes each, shipped by @basnijholt (fcc07b1), with keys counted (0b43956); evicted or oversized ones must be resent in full. A session-only cross-signing retry without retained keys gets
M_MISSING_PARAMasking for the full body (cdde2f4). -
With appreciation to @basnijholt for formatting and closure-borrow changes for October 3 nightly Clippy compatibility in (#621) and a steadier auto-accept-invites test in (#630), and to @SatvikMishra08 for fixing
federate_created_roomsdocumentation in (#643), reported by @edenworky in (#642). -
After four consecutive federation failures, a content-related error triggers transaction splitting by room. A failing room can be parked after another succeeds, with backoff from one hour to 24 hours, doubling each time. Connection failures and rate limits do not trigger splitting. Admin queries expose parked rooms (cffd295, bc0f9a0, a60622d, b492082, 9af7242, c449ad2).
-
Signed federation requests stop following HTTP redirects, so peers must serve them at the selected destination, and legacy media downloads and thumbnails ask remote origins for bytes. URL preview fetches and local object-store redirects are unaffected (021841f, 344d2c5).
-
SSO sign-in asks before sending its login token to an unlisted destination: anything outside the configured client origin and
oidc_registration_allowed_redirect_hosts(which accepts private-use schemes) gets a Continue sign-in page naming user and destination. Provider chains carry the account internally instead of as a redirect login token, checking the browser cookie at every step even wherecheck_cookieis off (7d83687). -
Federation preserves sibling changes sharing preceding snapshots through state-event fork identities. Resolution loads complete fork state, prepares resolved state before installation and batches forward-extremity replacements under the room state lock (9fcefae, 241ece9, bb8f3c1, 0b403d7, 5f048d8).
-
Shutdown-interrupted federation work avoids ordinary event-failure recording. Incoming transactions warn on missing PDUs; auth-chain failures remain distinct from fallback-eligible fork-state failures (14239a1, 92e5363, cb3915a, 8d3f6a9).
-
Federation SRV routing preserves request names while resolving target hosts/ports and stripping DNS trailing dots. Malformed remote names get quieter diagnostics; local resolver timeouts and connection exhaustion still warn/error (a8e41c3, 5045ab9).
-
/eventsrespects requester visibility; previews expose history world-readable at the event. Own join/invite events use their resulting membership. Missingfromstarts at the current stream position; malformed cursors returnM_INVALID_PARAM. The 50-event cap does not bound hidden-event scans (1803907, 23ef4fd, 73d60b1, 80e45ce). Read receipts must target a visible same-room event;m.fully_readis not covered (0d7794f). -
Remote profile refreshes must fit
max_remote_profile_fields(default 100) and 64 KiB whole, or the cache stays unchanged; filter and sync field selections are capped bymax_profile_fields_per_request(default 64). Both are reloadable; larger inputs accepted before are now refused. Refreshes keep stored null fields, which count toward the limits and can be replaced, and incremental Sliding Sync profile logs read only joined rooms (0933f37, 5490f37, 2059b2e). -
Multi-room search limits merged pages to 10 results by default, capped at 100. Pagination uses the last returned event's count instead of skip offsets. Results are bounded; total index work is not (2431c63).
-
Concurrent claims cannot reuse one-time keys or OAuth authorization codes within one running server. Codes are consumed before validation, including invalid exchanges (25dbbb5, 210297c).
-
Registration reserves names of accountless former admin-room members, returning
M_USER_IN_USEbefore side effects. Existing accounts and the server user remain exempt (cc6dfd4, 9993cb5). -
Admin-room commands require
m.text, excluding notices/emotes/captions. Outside-room escaped commands now default off (was on); reloadableadmin_escape_commands = trueretains them. Ordinary admin-room!admincommands remain unchanged. A shutdown interrupt before worker startup leaves the queue closed (a7ac11f, aa34a66, 08c68ee). -
Required email verification plus configured SMTP satisfies open-registration guards without tokens. Config display conceals
smtp.connection_uricredentials and hides unparsable/hostless URIs entirely (e8f6903, 2398708). -
LDAP-origin interactive authentication skips local-password lookup; the LDAP build feature and
ldap.enableremain required (6527b26). -
Space hierarchy cache entries record local or remote provenance, so remote answers no longer serve as local authority, and participation lookup errors no longer read as absence (5b2d534).
-
A federation destination already sending returns busy before peer-status work, sender workers return to tokio's cooperative budgeting, and presence scans hand their upper counter bound to RocksDB (48487fa, 436be8a).
-
Database seek commands destroy iterator state before releasing map and engine owners, including queued cancellation before execution (0b14469).
-
Debian packages link the system glibc on a Debian trixie baseline, declaring that dependency through cargo-deb; C++ and other libraries stay static. RocksDB moves to ThinLTO; packaging waits for passing tests (002f585, abd9837, 3e991df).
-
Element Web checks reject empty first passes and out-of-test errors, clear stale reports and preserve exit status. Browser/pnpm dependencies align; legacy-crypto known-failure coverage expands without fixing client behavior. Docker unit/integration binaries stop after ten minutes plus 30-second grace; benchmarks/Valgrind are uncapped. Debian checks debhelper markers; startup tests cover disabled federation (29bd18d, a3eb734, 0d3cf15, 4ac1a2c, 97a6378).
-
@basnijholt landed several more fixes: appservices reach other users' account data only within their own namespace and the delete routes (MSC3391) refuse
m.fully_readandm.push_rules(#626, 7e9466e); per-user room scans stop at the user ID boundary, so@alice:example.orgno longer matches@alice:example.org.other(#633); a leave without membership records no departure (#631); and Sliding Sync caps each room'stimeline_limitat 100 (#637).