Commit Graph
25904 Commits
Author SHA1 Message Date
Denis Kasak 7f4c6901ac Document the permissions blocks in CI workflows 2026-07-14 17:04:25 +02:00
Denis Kasak 7b2aae1016 Remove an unused issues permission from the triage-incoming.yml workflow 2026-07-14 17:04:25 +02:00
Denis Kasak 70b0e952c6 Fix broad permissions in the fix_lint.yaml workflow 2026-07-14 17:04:25 +02:00
Denis Kasak 9ef0a18fcb Add a documented exception for zizmor's cache-poisoning for tests.yml 2026-07-14 17:04:25 +02:00
Denis Kasak 7fa08f3005 Fix the mypy cache key in tests.yml
`github.context` isn't defined in this context, so the entire
`github.context.sha` expression evaluates to an empty string. As the
comment above it says, the recipe was cribbed from a TypeScript script
where `github.context` *is* defined, unlike within a GHA workflow file.

The correct replacement in a workflow file is `github.sha`.
2026-07-14 17:04:25 +02:00
Denis Kasak c9058d8dbf Skip the docker layer cache when building debs from a tag 2026-07-14 17:04:25 +02:00
Denis Kasak 7e28b289a9 Pin the postgres image version in tests.yml 2026-07-14 16:32:03 +02:00
Denis Kasak 7558a46faf Avoid interpolating expressions into scripts in CI workflows 2026-07-14 16:32:03 +02:00
Denis Kasak 7438953ff3 Explicitly ignore the artipacked zizmor lint for fix_lint.yaml 2026-07-14 16:32:03 +02:00
Denis Kasak f301669882 Don't persist git credentials after checkout in CI workflows 2026-07-14 16:31:25 +02:00
Denis Kasak 5fb744a59f Avoid interpolating expressions into scripts in the release-artifacts.yml workflow 2026-07-14 15:52:19 +02:00
Denis Kasak 392e9fd447 Fix broad permissions in the release-artifacts.yml workflow 2026-07-14 15:52:19 +02:00
Denis Kasak 7a07dded5a Fix code injection in the release-artifacts.yml workflow 2026-07-14 15:52:19 +02:00
Denis Kasak 5776c401b4 Add a global read-only permission block to push_complement_image.yml 2026-07-14 15:52:19 +02:00
Denis Kasak 194168aecb Globally restrict permissions in docker.yml and relax only at job level 2026-07-14 15:52:19 +02:00
Denis Kasak 215068374b Make permissions block global in triage_labelled.yml 2026-07-14 15:52:19 +02:00
Denis Kasak 2e8f48c464 Fix broad permissions in the docs-pr.yaml workflow 2026-07-14 15:52:19 +02:00
Denis Kasak 935557d5cb Fix broad permissions in the fix_lint.yaml workflow 2026-07-14 15:52:19 +02:00
Denis Kasak 252e8f833d Fix broad permissions in the poetry_lockfile.yaml workflow 2026-07-14 15:52:19 +02:00
Denis Kasak 8dd4a5ffbf Fix broad permissions in the schema.yaml workflow 2026-07-14 15:52:19 +02:00
Denis Kasak 4fd159d099 Fix broad permissions in the latest_deps.yml workflow 2026-07-14 15:52:19 +02:00
Denis Kasak 2e43b56284 Fix broad permissions in the triage-incoming.yml workflow 2026-07-14 15:52:19 +02:00
Denis Kasak be13e1e506 Fix broad permissions in the twisted_trunk.yml workflow 2026-07-14 15:52:19 +02:00
Denis Kasak dfaa9fda62 Fix broad permissions in the tests.yml workflow 2026-07-14 15:52:19 +02:00
Denis Kasak 47ddad6e90 Fix broad permissions in the docs.yaml workflow 2026-07-14 15:52:19 +02:00
dependabot[bot]andGitHub 1979fcca52 Bump actions/checkout from 6.0.3 to 7.0.0 (#19921)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-14 11:19:15 +00:00
Andrew MorganandGitHub 6bfec47c47 Revert "Fix flaky 3PID inhibit error unit tests" (#19916) 2026-07-14 09:46:28 +00:00
dependabot[bot]andGitHub ff7c3b9418 Bump actions/cache from 5.0.5 to 6.1.0 (#19920)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-14 09:25:28 +00:00
dependabot[bot]andGitHub 60aa4466c8 Bump the minor-and-patches group with 2 updates (#19919)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-14 09:25:07 +00:00
dependabot[bot]andGitHub 10f6370daf Bump bytes from 1.11.1 to 1.12.0 (#19918)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-14 09:24:39 +00:00
dependabot[bot]andGitHub 1b181ee813 Bump log from 0.4.32 to 0.4.33 in the patches group (#19917)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-14 09:24:11 +00:00
dependabot[bot]andGitHub aafb1fabcf Bump golang.org/x/crypto from 0.51.0 to 0.52.0 in /complement (#19925)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-14 09:04:15 +00:00
bf0f4f6a9d Support MSC4446: allow moving fully read markers backwards (#19663)
Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
2026-07-13 16:12:26 +00:00
WesselandGitHub c93d5230c0 Honor de.sorunome.msc2409.push_ephemeral flag in appservice handler (#19928) 2026-07-13 16:06:17 +00:00
Olivier 'reivilibreandGitHub 292bbb8f30 Fix a bug causing device list pruning to skip some rows when the transaction gets retried. (#19947)
Introduced in: #19473

Noticed in:
https://github.com/element-hq/synapse/pull/19556#discussion_r3505783541

I have not experienced the bug in the real world, it's just something I
noticed by reading.

--

Fix bug in `_prune_device_lists_changes_in_room` when transaction is
retried
The `nonlocal` variable is a footgun as it increments the counter even
though the transaction did not commit yet and may still be retried.

---------

Signed-off-by: Olivier 'reivilibre <oliverw@matrix.org>
2026-07-13 13:32:09 +01:00
SandroandGitHub 733620acd7 Fix SYNAPSE_ASYNC_IO_REACTOR=1 on Python 3.14 (#19949) 2026-07-13 09:39:56 +00:00
Erik JohnstonandGitHub be511b22a2 Send offline presence for stale states when presence is disabled (#19948)
If presence is disabled after having been enabled, the presence states
in the database (and hence on clients) are frozen at whatever they were
when presence was last enabled: nothing writes to the presence stream
any more and /sync omits the presence section entirely, so clients show
the old presence states forever.

Fix this in two parts:

1. At startup, if presence is disabled but the database still contains
non-offline presence states, the presence writer sends out one final
round of updates marking those users as offline.

2. /sync no longer unconditionally omits presence when presence is
disabled: incremental syncs whose since token is behind the presence
stream still get the straggling updates. As the stream doesn't advance
while presence is disabled, clients catch up once and the check then
short-circuits to a token comparison.

Remote servers already handle this themselves by timing out our users
([`FEDERATION_TIMEOUT`](https://github.com/element-hq/synapse/blob/4d8905a15a417ed0054ec2533d243932d890bbbd/synapse/handlers/presence.py#L194-L198)),
so no federation changes are needed.

Note that this only fixes the issue if presence is fully disabled. If
set to `untracked` we still have the same issue, however since modules
would still write to presence we can't just clobber everything like we
do in this patch.
2026-07-13 10:08:57 +01:00
0bd28389b1 Improve caching for presence (#19939)
This does two things, first it adds a config flag to ignore rooms for
the purposes of presence routing.

Secondly, it changes the caching behaviour to try and improve the cache
hit ratio. Previously, the size of the `do_users_share_a_room` cache
(which stores pairs of users) needs to `O(n²)` for the number of online
users, which is infeasible for large servers.

Instead, we call `get_users_in_room` for both the syncing and updated
users. This sounds more expensive, but a) we will already have cached
the syncing user's rooms, and b) we will only calculate the updated
user's rooms once (rather than once per syncing user).

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 09:34:07 +01:00
c0c2b37d5e MSC4140: update error responses (#19539)
- Impose limit of scheduled delayed events
- Update error codes to match latest draft of MSC4140 

---------

Co-authored-by: Eric Eastwood <madlittlemods@gmail.com>
2026-07-11 01:23:33 +00:00
4d8905a15a Throttle sync-driven presence updates relayed to the presence writer (#19941)
Sync workers proxied a full `ReplicationPresenceSetState` call to the
presence writer on every sync request with `affect_presence=True` (via
`user_syncing`), and a `ReplicationBumpPresenceActiveTime` call on every
user action, even though the writer's presence timers only need feeding
every `SYNC_ONLINE_TIMEOUT` / `LAST_ACTIVE_GRANULARITY`. On busy clients
this amounts to tens of no-op replication calls per user per minute, and
the resulting per-update work is the dominant CPU cost on saturated
presence writers.

Track the last relayed `(state, timestamp)` per `(user, device)` on the
worker and suppress unchanged sync-driven repeats within a 25s relay
interval - deliberately below `SYNC_ONLINE_TIMEOUT` (30s) so the
writer's device `last_sync_ts`/`last_active_ts` timers stay fed and
users neither flap offline nor bounce `currently_active`. Genuine state
changes are relayed immediately, explicit (non-sync) set_state calls
always go through and reset the throttle, bumps that might un-idle a
device bypass it, and entries are evicted when a `USER_SYNC` stop is
sent so reconnecting devices are relayed afresh.

This gives the writer-CPU benefit that deployments currently obtain by
tightening `rc_presence` to ~1/29s, without dropping real presence
transitions.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Quentin Gliech <quenting@element.io>
2026-07-10 14:05:10 +01:00
Erik JohnstonandGitHub 87439e55b5 Fix storage type mismatches where values didn't match their column types (#19911)
A handful of places in the storage layer bound a value whose Python type
didn't match the declared column type — an `int` into a `TEXT` column,
or a `str` into a `BIGINT` column — and relied on psycopg2's loose
coercion to paper over the mismatch. These are latent correctness bugs:
they only work because the driver silently converts, and a stricter
driver that binds typed parameters rejects them outright.

Found during a Rust port of the database pool where the driver does not
coerce automatically.

Each fix binds the value with the type the column actually declares,
rather than depending on driver-specific coercion. All changes are
behaviour-preserving on psycopg2 and sqlite.

There is also a fix to the multi-writer id-gen tests where we forgot to
commit. This is tangential, but was found during the same effort.

  ### Changes

- **`device_lists_remote_extremeties.stream_id (TEXT)`** —
`_update_remote_device_list_cache_txn` (typed `stream_id: int`) bound an
int; store it as a string, matching the column and the sibling
`_update_remote_device_list_cache_entry_txn` (typed `stream_id: str`).
The old mismatch, when rejected, was swallowed inside the device-list
resync and hung `query_devices`.
- **`user_filters.filter_id (BIGINT)`** — `get_user_filter` bound the
raw `int | str` (a string, from sync requests). It already validates via
`int(filter_id)`; bind that int so it matches the column.
- **`rejections.last_check (TEXT)`** — both writers stored
`clock.time_msec()` (an int); store the timestamp as a string.
- **user-directory temp position** — the `populate_user_directory`
background update's staging column
`_temp_populate_user_directory_position.position` was `TEXT` but held an
int (read back into a `BIGINT` column). Declare it `BIGINT`. The temp
table is created and dropped within the background update, so there's no
migration.

- **`test_batched_state_group_storing`** — selected from
`state_group_edges` with a stringified `state_group`; bind the int
directly (the column is an integer).
- **Multi-writer id-generator tests** — constructing a
`MultiWriterIdGenerator` prunes stale `stream_positions` rows, but the
harness never committed, so the cleanup only survived because adbapi
keeps one connection per thread with its transaction open. Commit after
construction so it persists regardless of pool semantics (the delete is
legitimate work that should be committed anyway).
2026-07-10 10:38:43 +01:00
9bbd974b3e Add index to sliding_sync_connection_lazy_members on connection_position (#19923)
This speeds up the cascading delete from
`sliding_sync_connection_positions`, which without an index on
`connection_position` requires a sequential scan of the whole table for
each deleted position.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 10:37:38 +01:00
3f3edaf029 Make the presence state-machine timers configurable (#19942)
Adds `last_active_granularity`, `sync_online_timeout` and `idle_timeout`
options to the `presence` config section, controlling the previously
hard-coded `LAST_ACTIVE_GRANULARITY`, `SYNC_ONLINE_TIMEOUT` and
`IDLE_TIMER` constants (which remain as the defaults).

This is mainly useful on deployments that ratelimit how often syncs can
affect presence (`rc_presence`): the sync timeout must exceed the
ratelimit interval or users flap offline between syncs, so tuning down
presence traffic currently requires squeezing under the fixed 30s limit.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 17:21:50 +01:00
Eric EastwoodandGitHub 365df86536 Fix Complement test flake when restarting Synapse workers (cross-test pollution caused by nginx upstreams being temporarily unavailable) (#19936)
Fix https://github.com/element-hq/synapse/issues/19907

The flake manifested as a failure pointing at
`TestMessagesOverFederation/Backfill_from_nearby_backward_extremities_past_token`
but was actually caused by some cross-test pollution from an earlier
test (`TestOIDCProviderUnavailable`) causing some workers to be
temporarily unavailable.

As explained in
https://github.com/element-hq/synapse/issues/19907#issuecomment-4917362461,

> ### Cross-test pollution
> 
> When I point an LLM at the logs, it points to
`TestOIDCProviderUnavailable` being the culprit because of the server
restarts polluting this test. When this flake happens, we can indeed see
that `TestOIDCProviderUnavailable` runs before
`TestMessagesOverFederation`.
> 
> ```
> PASS TestEventBetweenMakeJoinAndSendJoinIsNotLost 15.08s
> PASS TestFederation/parallel/HS2_->_HS1 1.5s
> PASS TestFederation/parallel/HS1_->_HS2 1.51s
> PASS TestFederation/parallel 0s
> PASS TestFederation 15.12s
> PASS TestOIDCProviderUnavailable//login/sso/redirect_shows_HTML_error
0.02s
> PASS TestOIDCProviderUnavailable 8.62s
> FAIL
TestMessagesOverFederation/Backfill_from_nearby_backward_extremities_past_token
0.89s
> FAIL TestMessagesOverFederation 1.1s
> PASS TestSynapseVersion/Synapse_version_matches_current_git_checkout
0.97s
> PASS TestSynapseVersion 0.97s
> ```
> 
> The pollution happens because we enable
[`COMPLEMENT_ENABLE_DIRTY_RUNS`](https://github.com/element-hq/synapse/blob/c63d77a79d7157f26f849684520ba9e99f4d07c0/scripts-dev/complement.sh#L309-L311)
([docs](https://github.com/matrix-org/complement/blob/0e6f8552ff0c99fddb97222399efed3e1f0cb91a/ENVIRONMENT.md#complement_enable_dirty_runs))
which means Complement will reuse deployments (shares homeservers
between tests).
> 
> During the `TestOIDCProviderUnavailable` test, there are some stray
federation requests that hit the homeserver while it's still booting
which marks the nginx upstream as unavailable for 10 seconds. nginx has
a default of
[`max_fails=1`](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#max_fails)
and
[`fail_timeout=10s`](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#fail_timeout).
Then when `TestMessagesOverFederation` starts, we're still in the 10
second unavailable window and nginx doesn't even try to connect at all.
> 
> <details>
> <summary>LLM summary of the logs and how this happens in
practice</summary>
> 
> 1. **19:52:10–11** — the previous federation test finishes:
`user-3:hs2` does a faster-join (`send_join?omit_members=true`) to
`!YnyCRpCLIpimppIreR:hs1`, so `hs2` kicks off a
`sync_partial_state_room` background resync that is still running when
the test ends.
> 2. **19:52:11.6** — `TestOIDCProviderUnavailable` starts and calls
`deployment.StopServer(t, "hs1")` / `StartServer`
(`complement/tests/oidc_test.go:78-80`) to apply an OIDC config
fragment. `hs1` gets `SIGTERM`; new supervisord at 19:52:13.3. `hs2` is
not restarted and keeps retrying its unfinished work.
> 3. **19:52:14** — `hs1`'s nginx is up, but the Synapse workers aren't:
`federation_inbound` only listens on `18015` at 19:52:18.7,
`federation_reader` on `18016` at 19:52:19.0.
> 4. **19:52:15–16** — `hs2`'s retries arrive in that gap: `PUT
/_matrix/federation/v1/send/…` → `18015` refused; `GET
/state_ids/!YnyCRpCLIpimppIreR:hs1` → `18016` refused. With nginx
defaults (`max_fails=1`, `fail_timeout=10s`) and only one server per
upstream block, both upstreams are now marked down until ~19:52:25/26.
(Side casualty: `hs2`'s partial-state resync gives up — "We can't get
valid state history" — and puts `hs1` on a 10-minute federation
backoff.)
> 5. **19:52:21.5** — the failing test's `make_join` for
`@user-5-bob:hs2` reaches `hs1`'s nginx → `no live upstreams` → `502` →
the join fails, even though the worker has been listening for 2.5
seconds by then.
> 6. **19:52:22** — `TestSynapseVersion`'s `GET
/_matrix/federation/v1/version` hits the same dead upstream → `502` → it
fails too.
> 
> So it's a flake caused by a race between the OIDC test's container
restart, hs2's background federation retries, and nginx's passive
health-check — not anything wrong with the backfill logic under test.
> 
> </details>
> 
> 
> We can indeed confirm this suspicion with these logs
> 
> 
https://github.com/element-hq/synapse/actions/runs/28888070856/job/85701936736
(archive:
[85701936736.log](https://github.com/user-attachments/files/29809986/85701936736.log)):
> ```
> Error: 2026/07/07 19:52:20 [error] 34#34: *1 no live upstreams while
connecting to upstream, client: 172.18.0.3, server: localhost, request:
"PUT /_matrix/federation/v1/send/1783453927718 HTTP/1.1", upstream:
"http://federation_inbound/_matrix/federation/v1/send/1783453927718",
host: "hs1"
> ...
> 
> Error: 2026/07/07 19:52:21 [error] 33#33: *4 no live upstreams while
connecting to upstream, client: 172.18.0.3, server: localhost, request:
"GET
/_matrix/federation/v1/make_join/%21scAbyTQDdaauGYicpU%3Ahs1/%40user-5-bob%3Ahs2?ver=1&ver=2&ver=3&ver=4&ver=5&ver=6&ver=7&ver=8&ver=9&ver=10&ver=11&ver=12&ver=org.matrix.msc3757.10&ver=org.matrix.msc3757.11&ver=org.matrix.hydra.11
HTTP/1.1", upstream:
"http://federation_reader/_matrix/federation/v1/make_join/%21scAbyTQDdaauGYicpU%3Ahs1/%40user-5-bob%3Ahs2?ver=1&ver=2&ver=3&ver=4&ver=5&ver=6&ver=7&ver=8&ver=9&ver=10&ver=11&ver=12&ver=org.matrix.msc3757.10&ver=org.matrix.msc3757.11&ver=org.matrix.hydra.11",
host: "hs1"
> ```
> 
> The fix here would be to disable nginx's unavailable upstream behavior
by configuring `max_fails=0` in the upstream block:
https://github.com/element-hq/synapse/blob/c63d77a79d7157f26f849684520ba9e99f4d07c0/docker/configure_workers_and_start.py#L374-L378
2026-07-09 11:12:28 -05:00
Eric EastwoodandGitHub 9c6cca1ac9 Add clean deploy FIXME note for TestOIDCProviderUnavailable (#19938)
Tracked by https://github.com/element-hq/synapse/issues/19937
2026-07-09 11:09:59 -05:00
b4deb24be6 Use a CPU-time budget for test_lock_contention to fix postgres flakiness (#19929)
`test_lock_contention` is a performance-regression canary (#16840): the
pathological behaviour it guards against spent ~30s spinning the CPU, vs
~0.5s when healthy. The 5s wall-clock alarm it used was calibrated on
SQLite, but against PostgreSQL a healthy run already takes 3-4s of
wall-clock time (500 sequential acquire/release cycles, each a real
database round-trip), so any CI load pushed it over the limit.

Add a `cpu_time` mode to `tests/utils.py`'s test_timeout, implemented
with
[`setitimer(ITIMER_PROF)`](https://docs.python.org/3/library/signal.html#signal.setitimer),
which budgets process CPU time instead of wall-clock time. Time spent
blocked on the database or lost to a loaded CI runner no longer counts,
while a regression to CPU-spinning still trips the alarm mid-spin. A
healthy run costs <1s of CPU on either database engine; the budget is
10s.

This also subsumes the RISC-V wall-clock carve-out from #18430, which is
removed.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 10:40:53 +01:00
Eric EastwoodandGitHub c63d77a79d Rust database access via Python database connection pool v2 (#19878)
This is a stepping stone before we can go full Rust everywhere. We're
providing a generic interface as we want database access to work in
Synapse and `synapse-rust-apps`. In `synapse-rust-apps`, we will use a
`tokio-postgres` based database connection pool so it's full Rust.

We want to avoid the situation where we have two database connection
pools (one for Python, one for Rust) as we've run into connection
exhaustion problems on Matrix.org before.

As an example of using it and sanity check for all this work (including
tests), I've also ported over the `/versions` handler to the Rust side
with database access. The `/versions` endpoint is the simplest endpoint
I could find that still had some database access. Hopefully the refactor
on `/versions` isn't that controversial as it's not really the point of
this PR. We can always remove it from this PR but it's just here as a
sanity check that all of this works.


### Why `runInteraction(...)`?

Using the same `runInteraction` pattern that we already have in Synapse
means we can port over existing Synapse code/endpoints without much
thought. But this pattern also makes sense because we want[^1]
transactions to have repeatable-read isolation (easy to think about,
less foot-guns). Having everything thappen in a function callback means
we can do retries for serialization/deadlock errors.

[^1]: To note: Ideally, we'd want the least isolation possible but the
problem is that there is no tooling to yell at you when your
queries/logic is wrong so repeatable-read isolation is a great balance.


> When an application receives this error message, it should abort the
current transaction and retry the whole transaction from the beginning.
The second time through, the transaction will see the
previously-committed change as part of its initial view of the database,
so there is no logical conflict in using the new version of the row as
the starting point for the new transaction's update.
> 
> Note that only updating transactions might need to be retried;
read-only transactions will never have serialization conflicts.
>
> *--
https://www.postgresql.org/docs/current/transaction-iso.html#XACT-REPEATABLE-READ*


As a note, this strategy is less of an impedance mismatch (aligns more
closely) with Synapse so the glue code for the `python_db_pool` should
also be simpler.


### How does this interact with logcontext (`LoggingContext`)?

See [docs on log
contexts](https://github.com/element-hq/synapse/blob/4e9f7757f17ba81b8747b7f8f9646d17df145aa3/docs/log_contexts.md)
for more background.

We already support normal logging from Rust -> Python with `pyo3-log`
and `log` but as soon as we pass a thread boundary, everything is logged
against the `sentinel` log context. Normally, we want logs and CPU/DB
usage correlated with the request that spawned the work.

You can see how I took a stab at fixing this in
https://github.com/element-hq/synapse/pull/19846 by capturing the
logcontext in a Tokio task local and re-activating as necessary. For
example, in that PR, I reactivated the logcontext in
`run_python_awaitable(...)` which we use to call `runInteraction(...)`
from the Rust side which means all of the database usage is correlated
with the request as expected. It also means any `log:info!(...)` done in
`run_interaction(...)` is correlated correctly. But there needs to be a
better story for when you want to log everywhere else.

I haven't explored tracking CPU usage on the Rust side.

I've left all of this out of this PR as I think it will be better to
tackle this as a dedicated follow-up. For example, I'm thinking about
instead creating a new `LoggingContext` with the `parent_context` set to
the calling context and try to avoid needing to call
`set_current_context(...)` on the Python side where possible (like
tracking CPU).



### Testing strategy

Added some tests that exercise some `async` Rust handlers for the
`/versions` endpoint:
```
SYNAPSE_TEST_LOG_LEVEL=INFO poetry run trial tests.rest.client.test_versions.VersionsTestCase
```

Real-world:

 1. `poetry run synapse_homeserver --config-path homeserver.yaml`
 1. `GET http://localhost:8008/_matrix/client/versions`
2026-07-07 13:07:35 -05:00
Olivier 'reivilibre ec6c53e7df Merge branch 'master' into develop 2026-07-07 15:01:22 +01:00
Olivier 'reivilibre be8d900438 1.156.0 v1.156.0 2026-07-07 14:20:45 +01:00
f8fefb09bd Re-expose the standalone format_event_* transforms for backwards compat (#19922)
The port of event serialization to Rust (#19837) removed
`format_event_raw`, `format_event_for_client_v1`,
`format_event_for_client_v2` and
`format_event_for_client_v2_without_room_id` from
`synapse.events.utils`, but there are modules in the wild that import
them from there.

Reimplement them as standalone pyfunctions in Rust, operating directly
on the Python dict so the original semantics are preserved exactly
(in-place mutation, returning the same dict, arbitrary non-JSON values
passing through, KeyError on a missing `unsigned` in the v1 format), and
re-export them from `synapse.events.utils`.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 13:47:03 +01:00