Commit Graph

149 Commits

Author SHA1 Message Date
Erik Johnston ee733ae2f6 Correctly handle room_id/state_key being null
These should either be absent or strings. We cannot use `Option` as that
does not differentiate between `null` and an absent field.
2026-05-27 15:51:03 +01:00
Erik Johnston 8fd10e4143 s/test_room_id_for_create_event/test_room_id_for_create_event_format_v4/ 2026-05-27 13:38:25 +01:00
Erik Johnston ae3a03fc4f Sanity check state_key is None instead of some empty string 2026-05-27 13:37:22 +01:00
Erik Johnston 9fa851d7f5 Comment on why state_key_attr 2026-05-27 13:31:59 +01:00
Erik Johnston feaa364f86 Rename bad_key to unknown_key 2026-05-27 13:14:57 +01:00
Erik Johnston d6258ff227 Make test_redact_missing_type clearer 2026-05-27 13:13:42 +01:00
Erik Johnston 925f5a1294 Handle MSC4242 in redact 2026-05-27 12:57:30 +01:00
Erik Johnston 15462d1d44 Lint 2026-05-27 12:57:16 +01:00
Erik Johnston f1ae164ab9 Remove redacted_because TODO 2026-05-27 12:53:46 +01:00
Erik Johnston 51ace0f588 Don't check PDU size in compute_reference_hash 2026-05-27 12:52:45 +01:00
Erik Johnston 1479a94ee7 Pop AGE_TS when redacting as well 2026-05-27 12:47:45 +01:00
Erik Johnston ecf8e8c869 Comment why type_ 2026-05-27 12:05:15 +01:00
Erik Johnston 309f826d21 Note that third-party modules rely on deprecated functions 2026-05-27 12:04:04 +01:00
Erik Johnston ee669251c1 Clean up .sticky_duration() 2026-05-27 12:02:50 +01:00
Erik Johnston ffe6c8b430 Add MSC4354_STICKY constant 2026-05-27 11:51:03 +01:00
Erik Johnston 025f1a479e Fix tests 2026-05-21 14:56:48 +01:00
Erik Johnston c9e9d76e86 Make MAX_DURATION a SynapseDuration 2026-05-21 14:45:52 +01:00
Erik Johnston b99fd7329d s/debug_assert/ensure 2026-05-21 14:37:30 +01:00
Erik Johnston 1bbb0332bd Drop SimpleAuthPrevEvents 2026-05-21 14:36:19 +01:00
Erik Johnston 894d223132 Mention where get_templated_pdu_json is used 2026-05-21 14:34:55 +01:00
Erik Johnston aae2de1cf0 Update get_dict docstring 2026-05-21 14:32:11 +01:00
Erik Johnston 24e7f8516a Rename to parsed_event 2026-05-21 14:31:26 +01:00
Erik Johnston fc65b1d5bc Add room version to error 2026-05-21 14:27:14 +01:00
Erik Johnston 27e312ddaf Make JsonObject.object private 2026-05-21 14:26:02 +01:00
Erik Johnston 8e6e29cf1a Fix MAX_PDU_SIZE_BYTES 2026-05-21 14:15:02 +01:00
Erik Johnston e82ebe17b2 Small refactor to ensure we call 'validate' for all formats 2026-05-21 14:14:29 +01:00
Erik Johnston 69c8d226b8 Ensure we don't have stray auth_events or event_id fields unexpectedly set 2026-05-21 14:10:51 +01:00
Erik Johnston 71151640b2 Update error for auth_event_ids 2026-05-21 14:02:10 +01:00
Erik Johnston f5f7e7c729 Convert get_room_id_for_optional_room_id into match 2026-05-21 13:56:08 +01:00
Erik Johnston d4801f78a7 Docstring for type_state_key_tuple 2026-05-21 13:46:10 +01:00
Erik Johnston d7a8dc5e27 Comment which fields are mutable and immutable 2026-05-21 13:43:59 +01:00
Erik Johnston cdc09df65a s/on-the-wire/over-the-wire 2026-05-21 13:38:53 +01:00
Erik Johnston 6135aaca11 Add Event pyclass to Rust
Adds a single `Event` Rust pyclass that replaces the Python EventBase /
FrozenEventV{1,2,3,4,VMSC4242} hierarchy. The class is added but not yet
wired into Python — callers continue to use the existing Python classes
in this commit; the migration follows in the next commit.

The internals use an `FormattedEvent` over
`EventFormatV{1,2V3,4,VMSC4242}` structs sharing an `EventCommonFields`.
Format-specific behaviour (prev_event_ids, auth_event_ids, room_id
derivation for v12 create events, etc) is encapsulated per variant.
Event IDs are computed in the constructor for v3+ formats; v1/v2 use the
`event_id` field as-is.

Two supporting Rust modules are added at the same time:

- `events::constants` — string constants for event types, top-level
  fields, and per-event-type content fields, used to keep the redaction
  rules and field accessors readable.
- `events::utils` — `redact()`, `compute_event_reference_hash()`, and
  `calculate_event_id()`, ported from `synapse.crypto.event_signing` /
  `synapse.events.utils`.
2026-05-15 15:59:55 +01:00
Erik Johnston e028519772 Add helpers and visibility for the upcoming Event port
Small prerequisites for porting the Python EventBase hierarchy to Rust:

- duration: make `from_milliseconds` const and add an `IntoPyObject` impl
  for owned `SynapseDuration`, so the new Rust `Event.sticky_duration()`
  can return one directly to Python.
- internal_metadata: rename `copy()` to `deep_copy()` (matching the new
  naming used by the rest of the events module) and make `new()` callable
  from sibling modules.
- json_object: expose `object` as a `pub` field and add a `get_field`
  helper so the new Event class can read from it without going through
  Python.
- signatures, unsigned: add `deep_copy()` methods so the new Event class
  can implement its own deep-copy.
2026-05-15 15:59:03 +01:00
Erik Johnston ace8447037 Tidy up Rust RoomVersion structs (#19766)
This is in prep for using the room versions more from Rust.

Main changes:
- Change it so each room version is defined as a delta to the last one.
This is a cosmetic change that makes it easier to ensure the room
version definitions are correct (as they're defined as deltas from
previous versions).
- Move constants to `RoomVersion` constants, like `RoomVersion::V1`, for
convenience.
- Change visibility of various attributes.
2026-05-14 11:21:00 +01:00
Olivier 'reivilibre 1b0622fa99 Merge branch 'release-v1.153' into develop 2026-05-13 13:10:18 +01:00
Erik Johnston 5efeac44b2 Handle arbitrary sized integers in unsigned. (#19769)
Handle arbitrary sized integers in `unsigned` (and other Rust objects
that use `serde_json::Value`)
2026-05-13 11:28:06 +01:00
Erik Johnston c430c16df4 Port event content to Rust (#19725)
Based on #19708.

This is on the path to porting the entire event class to Rust, as
`event.content` will then return the new Rust class `JsonObject`.

This PR adds a pure Rust `JsonObject` class that is a `Mapping`
representing a json-style object. It uses `serde_json::Value` as its
in-memory representation and `pythonize` for conversion when a field is
looked up on the object.

I'm not thrilled with the name, but couldn't think of a better one.

This also adds `JsonObject` handling to the JSON serialisation functions
we use, as well as to the `freeze(..)` function.

Reviewable commit-by-commit.
2026-05-08 14:19:03 +01:00
Eric Eastwood 8dbbc4000b Commit stray Rust change that keeps popping up (rust/src/canonical_json.rs) (#19763)
(introduced in https://github.com/element-hq/synapse/pull/19739)

Seems like some automatic change from `poetry run ./scripts-dev/lint.sh`
2026-05-08 06:20:25 -05:00
Erik Johnston 23b8fcf85e Port Event.unsigned field to Rust (#19708)
Similar to #19706, let's port the `unsigned` field into a Rust class.

This does change things a bit in that we now define exactly what
unsigned fields that are allowed to be added to an event, and what
actually gets persisted. This should be a noop though, as we carefully
filter out what unsigned fields we allow in from federation, for example

As a side effect of this cleanup, I think this fixes handling
`unsigned.age` on events received over federation.
2026-05-06 18:51:42 +01:00
Erik Johnston 3e6bf10640 Port Event.signatures field to Rust (#19706)
This is another stepping stone in porting the event class fully to Rust.

The new `Signatures` class is relatively simple, as we actually don't
interact with it that much in the code. It does *not* implement
`Mapping` or `MutableMapping` as that takes quite a lot of effort that
we don't need, even though it would be more ergonomic.
2026-05-06 11:38:15 +01:00
Erik Johnston 76b4fdceed Add a canonical JSON impl (#19739)
This comes from
https://github.com/erikjohnston/rust-signed-json/blob/main/src/json.rs.
We need to be able to serialise canonical JSON in Rust to be able to
calculate event IDs once we port the event class to Rust.

We could instead make the above a properly published crate, but feels
easier to pull it into Synapse utils.
2026-04-28 17:46:03 +01:00
Kegan Dougal 15c03b9689 MSC4242: State DAGs (CSAPI) (#19424)
This implements [MSC4242: State
DAGs](https://github.com/matrix-org/matrix-spec-proposals/pull/4242),
without support for federation.

A general overview:
 - It adds a new room version and new event type.
 - It adds a new field `calculated_auth_event_ids` to internal metadata.
- It stores the state DAG via new state DAG edges / forward extremities
tables.
 - It adds new auth rules as per the MSC.
- It uses the new `prev_state_events` field instead of
`prev_event_ids()` when doing state resolution.

Complement tests: https://github.com/matrix-org/complement/pull/841

### Pull Request Checklist

<!-- Please read
https://element-hq.github.io/synapse/latest/development/contributing_guide.html
before submitting your pull request -->

* [x] Pull request is based on the develop branch
* [x] Pull request includes a [changelog
file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog).
The entry should:
- Be a short description of your change which makes sense to users.
"Fixed a bug that prevented receiving messages from other servers."
instead of "Moved X method from `EventStore` to `EventWorkerStore`.".
  - Use markdown where necessary, mostly for `code blocks`.
  - End with either a period (.) or an exclamation mark (!).
  - Start with a capital letter.
- Feel free to credit yourself, by adding a sentence "Contributed by
@github_username." or "Contributed by [Your Name]." to the end of the
entry.
* [x] [Code
style](https://element-hq.github.io/synapse/latest/code_style.html) is
correct (run the
[linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters))

---------

Co-authored-by: Eric Eastwood <erice@element.io>
2026-04-16 15:46:47 +00:00
Erik Johnston 2d015f78ea Convert EventInternalMetadata to use Arc<RwLock<_>> (#19669)
This moves the reference counting from PyO3 into standard Rust types,
allowing the class to be used natively from Rust without needing a
Python runtime.
2026-04-16 10:59:39 +01:00
Olivier 'reivilibre 52c05c5ca4 Introduce spam_checker_spammy internal event metadata. (#19453)
Follows: #19365

Part of: MSC4354 Sticky Events (experimental feature #19409)

This PR introduces a `spam_checker_spammy` flag, analogous to
`policy_server_spammy`, as an explicit flag
that an event was decided to be spammy by a spam-checker module.

The original Sticky Events PR (#18968) just reused
`policy_server_spammy`, but it didn't sit right with me
because we (at least appear to be experimenting with features that)
allow users to opt-in to seeing
`policy_server_spammy` events (presumably for moderation purposes).

Keeping these flags separate felt best, therefore.

As for why we need this flag: soon soft-failed status won't be
permanent, at least for sticky events.
The spam checker modules currently work by making events soft-failed.
We want to prevent spammy events from getting
reconsidered/un-soft-failed, so it seems like we need
a flag to track spam-checker spamminess *separately* from soft-failed.

Should be commit-by-commit friendly, but is also small.

---------

Signed-off-by: Olivier 'reivilibre <oliverw@matrix.org>
2026-04-15 16:53:23 +01:00
Quentin Gliech a778497acb Merge branch 'master' into develop 2026-04-07 15:43:26 +02:00
Quentin Gliech 09d83f3127 Fix KNOWN_ROOM_VERSIONS.__contains__ raising TypeError for non-string keys (#19649)
The Rust port of `KNOWN_ROOM_VERSIONS` (#19589) made `__contains__`
strict about key types, raising `TypeError` when called with `None`
instead of returning `False` like a Python dict would.
This broke `/sync` for rooms with a NULL `room_version` in the database.

```
  File "/home/synapse/src/synapse/handlers/sync.py", line 2628, in _get_room_changes_for_initial_sync
    if event.room_version_id not in KNOWN_ROOM_VERSIONS:
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: argument 'key': 'NoneType' object cannot be cast as 'str'
```
2026-04-07 12:12:01 +00:00
Noah Markert 8291a493c7 resolves #19403 Report the rust compiler version used in the prometheus metrics (#19643)
# What is done?
- resolves #19403
- Adds build-time Rust compiler detection and captures the rustc
--version value during the build.
- Exposes the captured compiler version from the Rust extension via a
new Python-callable function.
- Exports a new Prometheus metric for rustc version.

# How to test?
- compile `poetry install`
- add `enable_metrics: true` and 
```yaml
    resources:
    - compress: false
      names:
      - client
      - federation
      - metrics
```
to homeserver.yaml
- start synapse
- find the rustc version at `http://localhost:8008/_synapse/metrics`

### Pull Request Checklist

<!-- Please read
https://element-hq.github.io/synapse/latest/development/contributing_guide.html
before submitting your pull request -->

* [x] Pull request is based on the develop branch
* [x] Pull request includes a [changelog
file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog).
The entry should:
- Be a short description of your change which makes sense to users.
"Fixed a bug that prevented receiving messages from other servers."
instead of "Moved X method from `EventStore` to `EventWorkerStore`.".
  - Use markdown where necessary, mostly for `code blocks`.
  - End with either a period (.) or an exclamation mark (!).
  - Start with a capital letter.
- Feel free to credit yourself, by adding a sentence "Contributed by
@github_username." or "Contributed by [Your Name]." to the end of the
entry.
* [x] [Code
style](https://element-hq.github.io/synapse/latest/code_style.html) is
correct (run the
[linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters))

---------

Co-authored-by: Quentin Gliech <quenting@element.io>
2026-04-03 11:21:23 +02:00
Erik Johnston 539f708f32 Remove redacted_because from internal unsigned. (#19581)
This is a simplification so that `unsigned` only includes "simple"
values, to make it easier to port to Rust.

Reviewable commit-by-commit

Summary:

1. **Add `recheck` column to `redactions` table**
   
A new boolean `recheck` column (default true) is added to the
`redactions` table. This captures whether a redaction needs its sender
domain checked at read time — required for room v3+ where redactions are
accepted speculatively and later validated. When persisting a new
redaction, `recheck` is set directly from
`event.internal_metadata.need_to_check_redaction()`.
     
It's fine if initially we recheck all redactions, as it only results in
a little more CPU overhead (as we always pull out the redaction event
regardless).
                                                      
2. **Backfill `recheck` via background update**
   
A background update (`redactions_recheck`) backfills the new column for
existing rows by reading `recheck_redaction` from each event's
`internal_metadata` JSON. This avoids loading full event objects by
reading `event_json` directly via a SQL JOIN.
                              
3. **Don't fetch confirmed redaction events from the DB**
                              
Previously, when loading events, Synapse recursively fetched all
redaction events regardless of whether they needed domain rechecking.
Now `_fetch_event_rows` reads the `recheck` column and splits redactions
into two lists:
        - `unconfirmed_redactions` — need fetching and domain validation
- `confirmed_redactions` — already validated, applied directly without
fetching the event
      
This avoids unnecessary DB reads for the common case of
already-confirmed redactions.
4. **Move `redacted_because` population to `EventClientSerializer`**
Previously, `redacted_because` (the full redaction event object) was
stored in `event.unsigned` at DB fetch time, coupling storage-layer code
to client serialization concerns. This is removed from
`_maybe_redact_event_row` and moved into
`EventClientSerializer.serialize_event`, which fetches the redaction
event on demand. The storage layer now only sets
`unsigned["redacted_by"]` (the redaction event ID).
5. **Always use `EventClientSerializer`**
   
The standalone `serialize_event` function was made private
(`_serialize_event`). All external callers — `rest/client/room.py`,
`rest/admin/events.py, appservice/api.py`, and `tests` — were updated to
use `EventClientSerializer.serialize_event` / `serialize_events`,
ensuring
  `redacted_because` is always populated correctly via the serializer.
6. **Batch-fetch redaction events in `serialize_events`**
   
`serialize_events` now collects all `redacted_by` IDs from the event
batch upfront and fetches them in a single `get_events` call, passing
the result as a `redaction_map` to each `serialize_event` call. This
reduces N individual DB round-trips to one when serializing a batch of
events that includes redacted events.

---------

Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-26 09:18:08 +00:00
Erik Johnston f545aa4f33 Port RoomVersion to Rust (#19589)
Principally so that we can share the same room version configuration
between Python and Rust.

For the most part, this is a direct port. Some special handling has had
to go into `KNOWN_ROOM_VERSIONS` so that it can be sensibly shared
between Python and Rust, since we do update it during config parsing.

---------

Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-26 09:17:31 +00:00