diff --git a/CHANGES.md b/CHANGES.md index b5947c44..b6584143 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,34 @@ # Changelog +## Dendrite 0.15.1 (2025-08-13) + +### Bug fixes + - Fixed an issue which could cause Dendrite to become unresponsive for minutes at a time. (contributed by [viviicat](https://github.com/viviicat)) + - Fixed an issue which prevented joining v12 rooms in some circumstances. + - Fixed an issue which prevented sending invites to v12 rooms. + - Fixed an issue which prevented some clients from syncing v12 rooms. + - Fixed an issue where a single badly formed PDU would block entire transactions of PDUs being processed. See https://github.com/element-hq/synapse/issues/7543 for the related issue on Synapse. + +## Dendrite 0.15.0 (2025-08-12) + +### ⚠ Important + +This is a security release, adding support for [room version 12](https://matrix.org/blog/2025/08/security-release/). + +### Features + - Add support for [MSC4163](https://github.com/matrix-org/matrix-spec-proposals/pull/4163). + - Add support for [MSC3967](https://github.com/matrix-org/matrix-spec-proposals/pull/3967). + - Add support for room version 12. + +### Bug fixes + - Refactored NATS JetStream code to gracefully handle more potential errors. (contributed by [neilalexander](https://github.com/neilalexander)) + - Refactored NATS startup and readiness checking. (contributed by [neilalexander](https://github.com/neilalexander)) + - Updated NATS to 2.11.7. (contributed by [neilalexander](https://github.com/neilalexander)) + - Fixed an issue which could cause Dendrite to become unresponsive for minutes at a time. (contributed by [viviicat](https://github.com/viviicat)) + - Order events when backfilling to reduce the amount of unecessary `/state_ids` requests. + - Gracefully handle incorrect sync filter JSON. + - Fixed an issue which prevented device deletion working correctly. (contributed by [robinsdan](https://github.com/robinsdan)) + ## Dendrite 0.14.1 (2025-01-16) ### ⚠ Important diff --git a/README.md b/README.md index b9e53ced..2d6d6c9d 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,14 @@ [![Build status](https://github.com/element-hq/dendrite/actions/workflows/dendrite.yml/badge.svg?event=push)](https://github.com/element-hq/dendrite/actions/workflows/dendrite.yml) [![Dendrite](https://img.shields.io/matrix/dendrite:matrix.org.svg?label=%23dendrite%3Amatrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#dendrite:matrix.org) [![Dendrite Dev](https://img.shields.io/matrix/dendrite-dev:matrix.org.svg?label=%23dendrite-dev%3Amatrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#dendrite-dev:matrix.org) -Dendrite is a second-generation Matrix homeserver written in Go. -It intends to provide an **efficient**, **reliable** and **scalable** alternative to [Synapse](https://github.com/matrix-org/synapse): +Dendrite is a second-generation Matrix homeserver written in Go. It is currently in maintenance mode, +meaning only security fixes are being applied, for example [room version 12](https://matrix.org/blog/2025/08/security-release/). + +It intends to provide an **efficient** and **reliable** alternative to [Synapse](https://github.com/matrix-org/synapse): - Efficient: A small memory footprint with better baseline performance than an out-of-the-box Synapse. - Reliable: Implements the Matrix specification as written, using the - [same test suite](https://github.com/matrix-org/sytest) as Synapse as well as - a [brand new Go test suite](https://github.com/matrix-org/complement). -- Scalable: can run on multiple machines and eventually scale to massive homeserver deployments. + [same](https://github.com/matrix-org/sytest) test [suites](https://github.com/matrix-org/complement) as Synapse. Dendrite is **beta** software, which means: @@ -31,6 +31,10 @@ If you have further questions, please take a look at [our FAQ](docs/FAQ.md) or j - **[#dendrite-dev:matrix.org](https://matrix.to/#/#dendrite-dev:matrix.org)** - The place for developers, where all Dendrite development discussion happens - **[#dendrite-alerts:matrix.org](https://matrix.to/#/#dendrite-alerts:matrix.org)** - Release notifications and important info, highly recommended for all Dendrite server admins +Dendrite does not currently support the following MSCs, which impacts the ability to use Element X with Dendrite servers: + - [MSC4186](https://github.com/matrix-org/matrix-spec-proposals/pull/4186): Simplified Sliding Sync + - [MSC3861](https://github.com/matrix-org/matrix-spec-proposals/pull/3861): Next-gen auth OIDC + ## Requirements See the [Planning your Installation](https://element-hq.github.io/dendrite/installation/planning) page for @@ -83,36 +87,6 @@ $ ./bin/create-account --config dendrite.yaml --username alice Then point your favourite Matrix client at `http://localhost:8008` or `https://localhost:8448`. -## Progress - -We use a script called "Are We Synapse Yet" which checks Sytest compliance rates. Sytest is a black-box homeserver -test rig with around 900 tests. The script works out how many of these tests are passing on Dendrite and it -updates with CI. As of January 2023, we have 100% server-server parity with Synapse, and the client-server parity is at 93% , though check -CI for the latest numbers. In practice, this means you can communicate locally and via federation with Synapse -servers such as matrix.org reasonably well, although there are still some missing features (like SSO and Third-party ID APIs). - -We are prioritising features that will benefit single-user homeservers first (e.g Receipts, E2E) rather -than features that massive deployments may be interested in (OpenID, Guests, Admin APIs, AS API). -This means Dendrite supports amongst others: - -- Core room functionality (creating rooms, invites, auth rules) -- Room versions 1 to 10 supported -- Backfilling locally and via federation -- Accounts, profiles and devices -- Published room lists -- Typing -- Media APIs -- Redaction -- Tagging -- Context -- E2E keys and device lists -- Receipts -- Push -- Guests -- User Directory -- Presence -- Fulltext search - ## Contributing We would be grateful for any help on issues marked as diff --git a/go.mod b/go.mod index 6e6e5a4b..2bcc218a 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/coder/websocket v1.8.13 github.com/cretz/bine v0.2.0 github.com/dgraph-io/ristretto v0.2.0 - github.com/docker/docker v26.1.5+incompatible + github.com/docker/docker v28.3.3+incompatible github.com/docker/go-connections v0.5.0 github.com/eyedeekay/goSam v0.32.54 github.com/eyedeekay/onramp v0.33.8 @@ -25,7 +25,7 @@ require ( github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 - github.com/matrix-org/gomatrixserverlib v0.0.0-20250811193806-b7e0e0824751 + github.com/matrix-org/gomatrixserverlib v0.0.0-20250813150445-9f5070a65744 github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7 github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 github.com/mattn/go-sqlite3 v1.14.28 @@ -83,6 +83,8 @@ require ( github.com/blevesearch/zapx/v15 v15.4.2 // indirect github.com/blevesearch/zapx/v16 v16.2.4 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect @@ -109,6 +111,7 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/minio/highwayhash v1.0.3 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/sys/atomicwriter v0.1.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect diff --git a/go.sum b/go.sum index 662ae0d6..e90e6364 100644 --- a/go.sum +++ b/go.sum @@ -73,6 +73,10 @@ github.com/codeclysm/extract v2.2.0+incompatible h1:q3wyckoA30bhUSiwdQezMqVhwd8+ github.com/codeclysm/extract v2.2.0+incompatible/go.mod h1:2nhFMPHiU9At61hz+12bfrlpXSUrOnK+wR+KlGO4Uks= github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= @@ -88,8 +92,8 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WA github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v26.1.5+incompatible h1:NEAxTwEjxV6VbBMBoGG3zPqbiJosIApZjxlbrG9q3/g= -github.com/docker/docker v26.1.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= +github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -237,8 +241,8 @@ github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 h1:s7fexw github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91/go.mod h1:e+cg2q7C7yE5QnAXgzo512tgFh1RbQLC0+jozuegKgo= github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U= github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= -github.com/matrix-org/gomatrixserverlib v0.0.0-20250811193806-b7e0e0824751 h1:x1pC7Nt1Qb24q9WtPybMHWo2uVFTzCKtlUAzarju8bk= -github.com/matrix-org/gomatrixserverlib v0.0.0-20250811193806-b7e0e0824751/go.mod h1:b6KVfDjXjA5Q7vhpOaMqIhFYvu5BuFVZixlNeTV/CLc= +github.com/matrix-org/gomatrixserverlib v0.0.0-20250813150445-9f5070a65744 h1:5GvC2FD9O/PhuyY95iJQdNYHbDioEhMWdeMP9maDUL8= +github.com/matrix-org/gomatrixserverlib v0.0.0-20250813150445-9f5070a65744/go.mod h1:b6KVfDjXjA5Q7vhpOaMqIhFYvu5BuFVZixlNeTV/CLc= github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7 h1:6t8kJr8i1/1I5nNttw6nn1ryQJgzVlBmSGgPiiaTdw4= github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7/go.mod h1:ReWMS/LoVnOiRAdq9sNUC2NZnd1mZkMNB52QhpTRWjg= github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 h1:6z4KxomXSIGWqhHcfzExgkH3Z3UkIXry4ibJS4Aqz2Y= @@ -259,6 +263,10 @@ github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/internal/transactionrequest.go b/internal/transactionrequest.go index bd6e70ce..55ff3656 100644 --- a/internal/transactionrequest.go +++ b/internal/transactionrequest.go @@ -134,6 +134,8 @@ func (t *TxnReq) ProcessTransaction(ctx context.Context) (*fclient.RespSend, *ut } event, err := verImpl.NewEventFromUntrustedJSON(pdu) if err != nil { + /* Do not reject the entire transaction for a single bad PDU, that's dumb. + if _, ok := err.(gomatrixserverlib.BadJSONError); ok { // Room version 6 states that homeservers should strictly enforce canonical JSON // on PDUs. @@ -146,7 +148,7 @@ func (t *TxnReq) ProcessTransaction(ctx context.Context) (*fclient.RespSend, *ut Code: 400, JSON: spec.BadJSON("PDU contains bad JSON"), } - } + } */ util.GetLogger(ctx).WithError(err).Debugf("Transaction: Failed to parse event JSON of event %s", string(pdu)) continue } diff --git a/internal/version.go b/internal/version.go index 5df808d7..bb253f2d 100644 --- a/internal/version.go +++ b/internal/version.go @@ -17,7 +17,7 @@ var build string const ( VersionMajor = 0 - VersionMinor = 14 + VersionMinor = 15 VersionPatch = 1 VersionTag = "" // example: "rc1" diff --git a/roomserver/internal/input/input.go b/roomserver/internal/input/input.go index 475a38de..fb132338 100644 --- a/roomserver/internal/input/input.go +++ b/roomserver/internal/input/input.go @@ -101,9 +101,12 @@ type worker struct { roomID string subscription *nats.Subscription sentryHub *sentry.Hub + ephemeralSeq uint64 + // last seq we fully processed + durableSeq uint64 } -func (r *Inputer) startWorkerForRoom(roomID string) { +func (r *Inputer) startWorkerForRoom(roomID string, seq uint64) { v, loaded := r.workers.LoadOrStore(roomID, &worker{ r: r, roomID: roomID, @@ -112,6 +115,9 @@ func (r *Inputer) startWorkerForRoom(roomID string) { w := v.(*worker) w.Lock() defer w.Unlock() + + w.ephemeralSeq = seq + if !loaded || w.subscription == nil { streamName := r.Cfg.Matrix.JetStream.Prefixed(jetstream.InputRoomEvent) consumer := r.Cfg.Matrix.JetStream.Prefixed("RoomInput" + jetstream.Tokenise(w.roomID)) @@ -226,7 +232,8 @@ func (r *Inputer) Start() error { "", // This is blank because we specified it in BindStream. func(m *nats.Msg) { roomID := m.Header.Get(jetstream.RoomID) - r.startWorkerForRoom(roomID) + meta, _ := m.Metadata() + r.startWorkerForRoom(roomID, meta.Sequence.Stream) _ = m.Ack() }, nats.HeadersOnly(), @@ -264,7 +271,7 @@ func (w *worker) _next() { }) msgs, err := w.subscription.Fetch(1, nats.Context(ctx)) switch err { - case nil, nats.ErrTimeout, context.DeadlineExceeded, context.Canceled: + case nil: // Is the server shutting down? If so, stop processing. if w.r.ProcessContext.Context().Err() != nil { return @@ -272,14 +279,33 @@ func (w *worker) _next() { // Make sure that once we're done here, we queue up another call // to _next in the inbox. defer w.Act(nil, w._next) - - // If no error was reported, but we didn't get exactly one message, - // then skip over this and try again on the next iteration. - if len(msgs) != 1 { + case nats.ErrTimeout, context.DeadlineExceeded, context.Canceled: + // Is the server shutting down? If so, stop processing. + if w.r.ProcessContext.Context().Err() != nil { + return + } + // The context exceeded, so we've been waiting for more than a + // minute for activity in this room. At this point we will shut + // down the subscriber to free up resources. It'll get started + // again if new activity happens. + w.Lock() + // inside the lock, let's check if the ephemeral consumer saw something new! + // If so, we do have new messages after all, they just came at a bad time. + if w.ephemeralSeq > w.durableSeq { + w.Unlock() + w.Act(nil, w._next) return } + if err = w.subscription.Unsubscribe(); err != nil { + logrus.WithError(err).Errorf("Failed to unsubscribe to stream for room %q", w.roomID) + } + w.subscription = nil + w.Unlock() + return case nats.ErrConsumerDeleted, nats.ErrConsumerNotFound: + w.Lock() + defer w.Unlock() // The consumer is gone, therefore it's reached the inactivity // threshold. Clean up and stop processing at this point, if a // new event comes in for this room then the ordered consumer @@ -287,21 +313,20 @@ func (w *worker) _next() { if err = w.subscription.Unsubscribe(); err != nil { logrus.WithError(err).Errorf("Failed to unsubscribe to stream for room %q", w.roomID) } - w.Lock() w.subscription = nil - w.Unlock() return - default: // Something went wrong while trying to fetch the next event // from the queue. In which case, we'll shut down the subscriber // and wait to be notified about new room activity again. Maybe // the problem will be corrected by then. + // atomically clear the subscription and unsubscribe + w.Lock() + logrus.WithError(err).Errorf("Failed to get next stream message for room %q", w.roomID) if err = w.subscription.Unsubscribe(); err != nil { logrus.WithError(err).Errorf("Failed to unsubscribe to stream for room %q", w.roomID) } - w.Lock() w.subscription = nil w.Unlock() return @@ -310,10 +335,19 @@ func (w *worker) _next() { // Since we either Ack() or Term() the message at this point, we can defer decrementing the room backpressure defer roomserverInputBackpressure.With(prometheus.Labels{"room_id": w.roomID}).Dec() + // If no error was reported, but we didn't get exactly one message, + // then skip over this and try again on the next iteration. + if len(msgs) != 1 { + return + } + // Try to unmarshal the input room event. If the JSON unmarshalling // fails then we'll terminate the message — this notifies NATS that // we are done with the message and never want to see it again. msg := msgs[0] + meta, _ := msg.Metadata() + w.durableSeq = meta.Sequence.Stream + var inputRoomEvent api.InputRoomEvent if err = json.Unmarshal(msg.Data, &inputRoomEvent); err != nil { // using AckWait here makes the call synchronous; 5 seconds is the default value used by NATS diff --git a/roomserver/internal/perform/perform_invite.go b/roomserver/internal/perform/perform_invite.go index f609e6ae..615951bf 100644 --- a/roomserver/internal/perform/perform_invite.go +++ b/roomserver/internal/perform/perform_invite.go @@ -31,7 +31,11 @@ type QueryState struct { } func (q *QueryState) GetAuthEvents(ctx context.Context, event gomatrixserverlib.PDU) (gomatrixserverlib.AuthEventProvider, error) { - return helpers.GetAuthEvents(ctx, q.Database, event.Version(), event, event.AuthEventIDs()) + authEventIDs := event.AuthEventIDs() + if gomatrixserverlib.MustGetRoomVersion(event.Version()).DomainlessRoomIDs() { + authEventIDs = append(authEventIDs, "$"+event.RoomID().String()[1:]) + } + return helpers.GetAuthEvents(ctx, q.Database, event.Version(), event, authEventIDs) } func (q *QueryState) GetState(ctx context.Context, roomID spec.RoomID, stateWanted []gomatrixserverlib.StateKeyTuple) ([]gomatrixserverlib.PDU, error) { diff --git a/syncapi/streams/stream_pdu.go b/syncapi/streams/stream_pdu.go index e55ff168..4daa1afc 100644 --- a/syncapi/streams/stream_pdu.go +++ b/syncapi/streams/stream_pdu.go @@ -404,7 +404,9 @@ func (p *PDUStreamProvider) addRoomDeltaToResponse( // "state" section and kept in "timeline". sEvents := gomatrixserverlib.HeaderedReverseTopologicalOrdering( gomatrixserverlib.ToPDUs(removeDuplicates(delta.StateEvents, events)), - gomatrixserverlib.TopologicalOrderByAuthEvents, + // sorting by auth events is not stable unless we know the create and historical PL events, + // which we don't for deltas like this. + gomatrixserverlib.TopologicalOrderByPrevEvents, ) delta.StateEvents = make([]*rstypes.HeaderedEvent, len(sEvents)) var skipped int diff --git a/sytest-blacklist b/sytest-blacklist index f4c08545..72f62d5e 100644 --- a/sytest-blacklist +++ b/sytest-blacklist @@ -21,4 +21,8 @@ Guest users can accept invites to private rooms over federation # Tests Synapse specific behavior /state returns M_NOT_FOUND for an outlier -/state_ids returns M_NOT_FOUND for an outlier \ No newline at end of file +/state_ids returns M_NOT_FOUND for an outlier + +# this is a silly restriction as it basically stops servers from communicating, so we relax it +# see https://github.com/element-hq/synapse/issues/7543 +Server rejects invalid JSON in a version 6 room \ No newline at end of file