Merge remote-tracking branch 'origin/main' into url-preview

Signed-off-by: adnull <d.lexand@gmail.com>
This commit is contained in:
adnull 2025-08-16 13:27:21 +02:00
commit 57b5def1d1
53 changed files with 993 additions and 637 deletions

View file

@ -272,7 +272,7 @@ jobs:
run: |
sed -i '/HOMESERVER/c\ HOMESERVER: "dendrite",' cypress.config.ts
- name: "Run cypress tests"
uses: cypress-io/github-action@v6.7.10
uses: cypress-io/github-action@v6.10.0
with:
browser: chrome
start: npx serve -p 8080 ./element-web/webapp
@ -312,7 +312,7 @@ jobs:
run: |
sed -i '/HOMESERVER/c\ HOMESERVER: "dendritePinecone",' cypress.config.ts
- name: "Run cypress tests"
uses: cypress-io/github-action@v6.7.10
uses: cypress-io/github-action@v6.10.0
with:
browser: chrome
start: npx serve -p 8080 ./element-web/webapp

View file

@ -28,7 +28,6 @@ run:
# the dependency descriptions in go.mod.
#modules-download-mode: (release|readonly|vendor)
# output configuration options
output:
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
@ -41,7 +40,6 @@ output:
# print linter name in the end of issue text, default is true
print-linter-name: true
# all available settings of specific linters
linters-settings:
errcheck:
@ -72,22 +70,12 @@ linters-settings:
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
golint:
# minimal confidence for issues, default is 0.8
min-confidence: 0.8
gofmt:
# simplify code: gofmt with `-s` option, true by default
simplify: true
goimports:
# put imports beginning with prefix after 3rd-party packages;
# it's a comma-separated list of prefixes
#local-prefixes: github.com/org/project
gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 25
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true
dupl:
# tokens count to trigger issue, 150 by default
threshold: 100
@ -96,30 +84,17 @@ linters-settings:
min-len: 3
# minimal occurrences count to trigger, 3 by default
min-occurrences: 3
depguard:
list-type: blacklist
include-go-root: false
packages:
# - github.com/davecgh/go-spew/spew
misspell:
# Correct spellings using locale preferences for US or UK.
# Default is to use a neutral variety of English.
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
locale: UK
ignore-words:
# - someword
lll:
# max line length, lines longer will be reported. Default is 120.
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
line-length: 96
# tab width in spaces. Default to 1.
tab-width: 1
unused:
# treat code as a program (not a library) and report unused exported identifiers; default is false.
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
check-exported: false
unparam:
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
@ -167,7 +142,6 @@ linters:
- gosimple
- govet
- ineffassign
- megacheck
- misspell # Check code comments, whereas misspell in CI checks *.md files
- nakedret
- staticcheck
@ -182,22 +156,16 @@ linters:
- gochecknoinits
- gocritic
- gofmt
- golint
- gosec # Should turn back on soon
- interfacer
- lll
- maligned
- prealloc # Should turn back on soon
- scopelint
- stylecheck
- typecheck # Should turn back on soon
- unconvert # Should turn back on soon
- goconst # Slightly annoying, as it reports "issues" in SQL statements
disable-all: false
presets:
fast: false
issues:
# which files to skip: they will be analyzed, but issues from them
# won't be reported. Default value is empty list, but there is
@ -217,13 +185,6 @@ issues:
- bin
- docs
# List of regexps of issue texts to exclude, empty list by default.
# But independently from this option we use default exclude patterns,
# it can be disabled by `exclude-use-default: false`. To list all
# excluded by default patterns execute `golangci-lint run --help`
exclude:
# - abcdef
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
# Exclude some linters from running on tests files.

View file

@ -1,5 +1,39 @@
# Changelog
## Dendrite 0.15.2 (2025-08-15)
### Bug fixes
- Fixed an issue which could cause Dendrite to crash on startup if the room state lacked a create event.
## 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

View file

@ -8,7 +8,7 @@ This is the repository for Dendrite, a second-generation Matrix homeserver writt
We ask that everybody who contributes to this project signs off their contributions, as explained below.
We follow a simple 'inbound=outbound' model for contributions: the act of submitting an 'inbound' contribution means that the contributor agrees to license their contribution under the same terms as the project's overall 'outbound' license - in our case, this is Apache Software License v2 (see [LICENSE](./LICENSE)).
We follow a simple 'inbound=outbound' model for contributions: the act of submitting an 'inbound' contribution means that the contributor agrees to license their contribution under the same terms as the project's overall 'outbound' license - in our case, this is GNU Affero General Public License v3 (see [LICENSE](./LICENSE)). Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
In order to have a concrete record that your contribution is intentional and you agree to license it under the same terms as the project's license, we've adopted the same lightweight approach used by the [Linux Kernel](https://www.kernel.org/doc/html/latest/process/submitting-patches.html), [Docker](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), and many other projects: the [Developer Certificate of Origin](https://developercertificate.org/) (DCO). This is a simple declaration that you wrote the contribution or otherwise have the right to contribute it to Matrix:

View file

@ -3,7 +3,7 @@
#
# base installs required dependencies and runs go mod download to cache dependencies
#
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.22-alpine AS base
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.24-alpine AS base
RUN apk --update --no-cache add bash build-base curl git
#

View file

@ -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,9 +31,13 @@ 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://matrix-org.github.io/dendrite/installation/planning) page for
See the [Planning your Installation](https://element-hq.github.io/dendrite/installation/planning) page for
more information on requirements.
To build Dendrite, you will need Go 1.21 or later.
@ -53,7 +57,7 @@ The [Federation Tester](https://federationtester.matrix.org) can be used to veri
## Get started
If you wish to build a fully-federating Dendrite instance, see [the Installation documentation](https://matrix-org.github.io/dendrite/installation). For running in Docker, see [build/docker](build/docker).
If you wish to build a fully-federating Dendrite instance, see [the Installation documentation](https://element-hq.github.io/dendrite/installation). For running in Docker, see [build/docker](build/docker).
The following instructions are enough to get Dendrite started as a non-federating test deployment using self-signed certificates and SQLite databases:
@ -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
@ -121,7 +95,7 @@ all have related Sytests which need to pass in order for the issue to be closed.
code, you can quickly run Sytest to ensure that the test names are now passing.
If you're new to the project, see our
[Contributing page](https://matrix-org.github.io/dendrite/development/contributing) to get up to speed, then
[Contributing page](https://element-hq.github.io/dendrite/development/contributing) to get up to speed, then
look for [Good First Issues](https://github.com/element-hq/dendrite/labels/good%20first%20issue). If you're
familiar with the project, look for [Help Wanted](https://github.com/element-hq/dendrite/labels/help-wanted)
issues.
@ -130,12 +104,11 @@ issues.
Copyright 2017 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2017-2025 New Vector Ltd
Copyright 2017-2025 New Vector Ltd
This software is dual-licensed by New Vector Ltd (Element). It can be used either:
(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR
(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to).
Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses.

View file

@ -1,6 +1,6 @@
#syntax=docker/dockerfile:1.2
FROM --platform=${BUILDPLATFORM} ghcr.io/element-hq/dendrite-monolith:binaries AS build
FROM --platform=${TARGETPLATFORM} ghcr.io/element-hq/dendrite-monolith:binaries AS build
FROM alpine:latest
RUN apk --update --no-cache add curl

View file

@ -1,6 +1,6 @@
#syntax=docker/dockerfile:1.2
FROM --platform=${BUILDPLATFORM} ghcr.io/element-hq/dendrite-monolith:binaries AS build
FROM --platform=${TARGETPLATFORM} ghcr.io/element-hq/dendrite-monolith:binaries AS build
FROM alpine:latest
LABEL org.opencontainers.image.title="Dendrite (Yggdrasil demo)"

View file

@ -1,6 +1,6 @@
#syntax=docker/dockerfile:1.2
FROM golang:1.22-bookworm as build
FROM golang:1.24-bookworm as build
RUN apt-get update && apt-get install -y sqlite3
WORKDIR /build

View file

@ -8,7 +8,7 @@
#
# Use these mounts to make use of this dockerfile:
# COMPLEMENT_HOST_MOUNTS='/your/local/dendrite:/dendrite:ro;/your/go/path:/go:ro'
FROM golang:1.22-bookworm
FROM golang:1.24-bookworm
RUN apt-get update && apt-get install -y sqlite3
ENV SERVER_NAME=localhost

View file

@ -1,6 +1,6 @@
#syntax=docker/dockerfile:1.2
FROM golang:1.22-bookworm as build
FROM golang:1.24-bookworm as build
RUN apt-get update && apt-get install -y postgresql
WORKDIR /build

View file

@ -176,12 +176,6 @@ func createRoom(
roomVersion = candidateVersion
}
logger.WithFields(log.Fields{
"userID": userID.String(),
"roomID": roomID.String(),
"roomVersion": roomVersion,
}).Info("Creating new room")
profile, err := appserviceAPI.RetrieveUserProfile(ctx, userID.String(), asAPI, profileAPI)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("appserviceAPI.RetrieveUserProfile failed")
@ -197,6 +191,49 @@ func createRoom(
keyID := cfg.Matrix.KeyID
privateKey := cfg.Matrix.PrivateKey
verImpl := gomatrixserverlib.MustGetRoomVersion(roomVersion)
var createEventJSON json.RawMessage
if verImpl.DomainlessRoomIDs() {
// make the create event up-front so the roomserver can calculate the room NID to store.
var additionalCreators []string
if createRequest.Preset == spec.PresetTrustedPrivateChat {
additionalCreators = createRequest.Invite
}
createContent, err := roomserverAPI.GenerateCreateContent(ctx, createRequest.RoomVersion, userID.String(), createRequest.CreationContent, additionalCreators)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("GenerateCreateContent failed")
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("invalid create content"),
}
}
authEvents, _ := gomatrixserverlib.NewAuthEvents(nil)
identity, err := cfg.Matrix.SigningIdentityFor(userID.Domain())
if err != nil {
util.GetLogger(ctx).WithError(err).Error("Failed to get signing identity")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
createEvent, jsonErr := roomserverAPI.GeneratePDU(
ctx, gomatrixserverlib.MustGetRoomVersion(roomVersion),
gomatrixserverlib.FledglingEvent{
Type: spec.MRoomCreate,
Content: createContent,
},
authEvents, 1, "", identity, evTime, userID.String(), "", rsAPI,
)
if jsonErr != nil {
util.GetLogger(ctx).WithError(err).Error("Failed to make the create event")
return *jsonErr
}
createEventJSON = createEvent.JSON()
r := createEvent.RoomID()
roomID = &r
}
req := roomserverAPI.PerformCreateRoomRequest{
InvitedUsers: createRequest.Invite,
RoomName: createRequest.Name,
@ -204,6 +241,7 @@ func createRoom(
Topic: createRequest.Topic,
StatePreset: createRequest.Preset,
CreationContent: createRequest.CreationContent,
CreateEvent: createEventJSON,
InitialState: createRequest.InitialState,
RoomAliasName: createRequest.RoomAliasName,
RoomVersion: roomVersion,
@ -217,6 +255,12 @@ func createRoom(
EventTime: evTime,
}
logger.WithFields(log.Fields{
"userID": userID.String(),
"roomID": roomID.String(),
"roomVersion": roomVersion,
}).Info("Creating new room")
roomAlias, createRes := rsAPI.PerformCreateRoom(ctx, *userID, *roomID, &req)
if createRes != nil {
return *createRes

View file

@ -411,10 +411,11 @@ func SetVisibility(
JSON: spec.InternalServerError{},
}
}
privileged := isPrivilegedCreator(req.Context(), rsAPI, roomID, *senderID)
// NOTSPEC: Check if the user's power is greater than power required to change m.room.canonical_alias event
power, _ := gomatrixserverlib.NewPowerLevelContentFromEvent(queryEventsRes.StateEvents[0].PDU)
if power.UserLevel(*senderID) < power.EventLevel(spec.MRoomCanonicalAlias, true) {
if !privileged && power.UserLevel(*senderID) < power.EventLevel(spec.MRoomCanonicalAlias, true) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("userID doesn't have power level to change visibility"),

View file

@ -11,6 +11,7 @@ import (
"crypto/ed25519"
"fmt"
"net/http"
"slices"
"time"
appserviceAPI "github.com/element-hq/dendrite/appservice/api"
@ -79,7 +80,8 @@ func SendBan(
if errRes != nil {
return *errRes
}
allowedToBan := pl.UserLevel(*senderID) >= pl.Ban
privileged := isPrivilegedCreator(req.Context(), rsAPI, roomID, *senderID)
allowedToBan := privileged || pl.UserLevel(*senderID) >= pl.Ban
if !allowedToBan {
return util.JSONResponse{
Code: http.StatusForbidden,
@ -118,6 +120,12 @@ func sendMembership(ctx context.Context, profileAPI userapi.ClientUserAPI, devic
false,
); err != nil {
util.GetLogger(ctx).WithError(err).Error("SendEvents failed")
if err.Error() == api.InputWasRejected {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("the event was rejected"),
}
}
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
@ -185,7 +193,8 @@ func SendKick(
if errRes != nil {
return *errRes
}
allowedToKick := pl.UserLevel(*senderID) >= pl.Kick || bodyUserID.String() == deviceUserID.String()
privileged := isPrivilegedCreator(req.Context(), rsAPI, roomID, *senderID)
allowedToKick := privileged || pl.UserLevel(*senderID) >= pl.Kick || bodyUserID.String() == deviceUserID.String()
if !allowedToKick {
return util.JSONResponse{
Code: http.StatusForbidden,
@ -680,3 +689,12 @@ func getPowerlevels(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI,
}
return pl, nil
}
// Returns true if the room is a room which supports privileged creators and the sender is a creator, else false.
func isPrivilegedCreator(ctx context.Context, rsAPI roomserverAPI.ClientRoomserverAPI, roomID string, senderID spec.SenderID) bool {
createEvent := roomserverAPI.GetStateEvent(ctx, rsAPI, roomID, gomatrixserverlib.StateKeyTuple{
EventType: spec.MRoomCreate,
StateKey: "",
})
return gomatrixserverlib.MustGetRoomVersion(createEvent.Version()).PrivilegedCreators() && slices.Contains(gomatrixserverlib.CreatorsFromCreateEvent(createEvent), string(senderID))
}

View file

@ -98,10 +98,12 @@ func SendRedaction(
}
}
privileged := isPrivilegedCreator(req.Context(), rsAPI, roomID, *senderID)
// "Users may redact their own events, and any user with a power level greater than or equal
// to the redact power level of the room may redact events there"
// https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid
allowedToRedact := ev.SenderID() == *senderID
allowedToRedact := ev.SenderID() == *senderID || privileged
if !allowedToRedact {
plEvent := roomserverAPI.GetStateEvent(req.Context(), rsAPI, roomID, gomatrixserverlib.StateKeyTuple{
EventType: spec.MRoomPowerLevels,

View file

@ -77,7 +77,6 @@ func SendEvent(
JSON: spec.UnsupportedRoomVersion(err.Error()),
}
}
if txnID != nil {
// Try to fetch response from transactionsCache
if res, ok := txnCache.FetchTransaction(device.AccessToken, *txnID, req.URL); ok {
@ -367,6 +366,12 @@ func generateSendEvent(
JSON: spec.InternalServerError{},
}
}
if proto.Type == spec.MRoomCreate && proto.StateKey != nil && *proto.StateKey == "" {
return nil, &util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.InvalidParam("cannot resend m.room.create event"),
}
}
identity, err := rsAPI.SigningIdentityFor(ctx, *validRoomID, *fullUserID)
if err != nil {
@ -424,8 +429,13 @@ func generateSendEvent(
if err = gomatrixserverlib.Allowed(e.PDU, provider, func(roomID spec.RoomID, senderID spec.SenderID) (*spec.UserID, error) {
return rsAPI.QueryUserIDForSender(ctx, *validRoomID, senderID)
}); err != nil {
code := 403
validationErr, ok := err.(*gomatrixserverlib.EventValidationError)
if ok {
code = validationErr.Code
}
return nil, &util.JSONResponse{
Code: http.StatusForbidden,
Code: code,
JSON: spec.Forbidden(err.Error()), // TODO: Is this error string comprehensible to the client?
}
}

View file

@ -14,6 +14,7 @@ import (
"time"
"github.com/matrix-org/gomatrix"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/tokens"
"github.com/matrix-org/util"
"github.com/prometheus/client_golang/prometheus"
@ -100,7 +101,7 @@ func SendServerNotice(
for _, membership := range []string{"join", "invite", "leave"} {
userRooms, queryErr := rsAPI.QueryRoomsForUser(ctx, *userID, membership)
if queryErr != nil {
return util.ErrorResponse(err)
return util.ErrorResponse(queryErr)
}
allUserRooms = append(allUserRooms, userRooms...)
}
@ -139,7 +140,7 @@ func SendServerNotice(
// create a new room for the user
if len(commonRooms) == 0 {
powerLevelContent := eventutil.InitialPowerLevelsContent(senderUserID.String())
powerLevelContent := eventutil.InitialPowerLevelsContent(gomatrixserverlib.MustGetRoomVersion(roomVersion), senderUserID.String())
powerLevelContent.Users[r.UserID] = -10 // taken from Synapse
pl, err := json.Marshal(powerLevelContent)
if err != nil {

View file

@ -23,7 +23,8 @@ import (
)
type upgradeRoomRequest struct {
NewVersion string `json:"new_version"`
NewVersion string `json:"new_version"`
AdditionalCreators []string `json:"additional_creators"`
}
type upgradeRoomResponse struct {
@ -43,6 +44,13 @@ func UpgradeRoom(
return *rErr
}
if r.NewVersion == "" {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.InvalidParam("missing version to upgrade to"),
}
}
// Validate that the room version is supported
if _, err := version.SupportedRoomVersion(gomatrixserverlib.RoomVersion(r.NewVersion)); err != nil {
return util.JSONResponse{
@ -59,7 +67,10 @@ func UpgradeRoom(
JSON: spec.InternalServerError{},
}
}
newRoomID, err := rsAPI.PerformRoomUpgrade(req.Context(), roomID, *userID, gomatrixserverlib.RoomVersion(r.NewVersion))
newRoomID, err := rsAPI.PerformRoomUpgrade(req.Context(), roomID, *userID, gomatrixserverlib.RoomVersion(r.NewVersion), r.AdditionalCreators)
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("PerformRoomUpgrade failed")
}
switch e := err.(type) {
case nil:
case roomserverAPI.ErrNotAllowed:

View file

@ -55,7 +55,7 @@ var latest, _ = semver.NewVersion("v6.6.6") // Dummy version, used as "HEAD"
// due to the error:
// When using COPY with more than one source file, the destination must be a directory and end with a /
// We need to run a postgres anyway, so use the dockerfile associated with Complement instead.
const DockerfilePostgreSQL = `FROM golang:1.22-bookworm as build
const DockerfilePostgreSQL = `FROM golang:1.24-bookworm as build
RUN apt-get update && apt-get install -y postgresql
WORKDIR /build
ARG BINARY
@ -99,7 +99,7 @@ ENV BINARY=dendrite
EXPOSE 8008 8448
CMD /build/run_dendrite.sh`
const DockerfileSQLite = `FROM golang:1.22-bookworm as build
const DockerfileSQLite = `FROM golang:1.24-bookworm as build
RUN apt-get update && apt-get install -y postgresql
WORKDIR /build
ARG BINARY

View file

@ -1,5 +1,2 @@
source "https://rubygems.org"
gem "github-pages", "~> 226", group: :jekyll_plugins
group :jekyll_plugins do
gem "jekyll-feed", "~> 0.15.1"
end
gem "github-pages", "~> 232", group: :jekyll_plugins

View file

@ -1,66 +1,65 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (6.0.6.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
zeitwerk (~> 2.2, >= 2.2.2)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
activesupport (8.0.2)
base64
benchmark (>= 0.3)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
logger (>= 1.4.2)
minitest (>= 5.1)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
uri (>= 0.13.1)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
base64 (0.2.0)
benchmark (0.4.1)
bigdecimal (3.2.2)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.11.1)
coffee-script-source (1.12.2)
colorator (1.1.0)
commonmarker (0.23.10)
concurrent-ruby (1.2.0)
dnsruby (1.61.9)
simpleidn (~> 0.1)
commonmarker (0.23.11)
concurrent-ruby (1.3.5)
connection_pool (2.5.3)
csv (3.3.5)
dnsruby (1.72.4)
base64 (~> 0.2.0)
logger (~> 1.6.5)
simpleidn (~> 0.2.1)
drb (2.2.3)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
ethon (0.15.0)
ethon (0.16.0)
ffi (>= 1.15.0)
eventmachine (1.2.7)
execjs (2.8.1)
faraday (1.10.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.3)
multipart-post (>= 1.2, < 3)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
ffi (1.15.5)
execjs (2.10.0)
faraday (2.13.4)
faraday-net_http (>= 2.0, < 3.5)
json
logger
faraday-net_http (3.4.1)
net-http (>= 0.5.0)
ffi (1.17.2-arm64-darwin)
ffi (1.17.2-x86_64-linux-gnu)
forwardable-extended (2.6.0)
gemoji (3.0.1)
github-pages (226)
github-pages-health-check (= 1.17.9)
jekyll (= 3.9.2)
jekyll-avatar (= 0.7.0)
jekyll-coffeescript (= 1.1.1)
jekyll-commonmark-ghpages (= 0.2.0)
jekyll-default-layout (= 0.1.4)
jekyll-feed (= 0.15.1)
gemoji (4.1.0)
github-pages (232)
github-pages-health-check (= 1.18.2)
jekyll (= 3.10.0)
jekyll-avatar (= 0.8.0)
jekyll-coffeescript (= 1.2.2)
jekyll-commonmark-ghpages (= 0.5.1)
jekyll-default-layout (= 0.1.5)
jekyll-feed (= 0.17.0)
jekyll-gist (= 1.5.0)
jekyll-github-metadata (= 2.13.0)
jekyll-github-metadata (= 2.16.1)
jekyll-include-cache (= 0.2.1)
jekyll-mentions (= 1.6.0)
jekyll-optional-front-matter (= 0.3.2)
@ -87,32 +86,34 @@ GEM
jekyll-theme-tactile (= 0.2.0)
jekyll-theme-time-machine (= 0.2.0)
jekyll-titles-from-headings (= 0.5.3)
jemoji (= 0.12.0)
kramdown (= 2.3.2)
jemoji (= 0.13.0)
kramdown (= 2.4.0)
kramdown-parser-gfm (= 1.1.0)
liquid (= 4.0.3)
liquid (= 4.0.4)
mercenary (~> 0.3)
minima (= 2.5.1)
nokogiri (>= 1.13.4, < 2.0)
rouge (= 3.26.0)
nokogiri (>= 1.16.2, < 2.0)
rouge (= 3.30.0)
terminal-table (~> 1.4)
github-pages-health-check (1.17.9)
webrick (~> 1.8)
github-pages-health-check (1.18.2)
addressable (~> 2.3)
dnsruby (~> 1.60)
octokit (~> 4.0)
public_suffix (>= 3.0, < 5.0)
octokit (>= 4, < 8)
public_suffix (>= 3.0, < 6.0)
typhoeus (~> 1.3)
html-pipeline (2.14.1)
html-pipeline (2.14.3)
activesupport (>= 2)
nokogiri (>= 1.4)
http_parser.rb (0.8.0)
i18n (0.9.5)
i18n (1.14.7)
concurrent-ruby (~> 1.0)
jekyll (3.9.2)
jekyll (3.10.0)
addressable (~> 2.4)
colorator (~> 1.0)
csv (~> 3.0)
em-websocket (~> 0.5)
i18n (~> 0.7)
i18n (>= 0.7, < 2)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 2.0)
kramdown (>= 1.17, < 3)
@ -121,27 +122,28 @@ GEM
pathutil (~> 0.9)
rouge (>= 1.7, < 4)
safe_yaml (~> 1.0)
jekyll-avatar (0.7.0)
webrick (>= 1.0)
jekyll-avatar (0.8.0)
jekyll (>= 3.0, < 5.0)
jekyll-coffeescript (1.1.1)
jekyll-coffeescript (1.2.2)
coffee-script (~> 2.2)
coffee-script-source (~> 1.11.1)
coffee-script-source (~> 1.12)
jekyll-commonmark (1.4.0)
commonmarker (~> 0.22)
jekyll-commonmark-ghpages (0.2.0)
commonmarker (~> 0.23.4)
jekyll (~> 3.9.0)
jekyll-commonmark-ghpages (0.5.1)
commonmarker (>= 0.23.7, < 1.1.0)
jekyll (>= 3.9, < 4.0)
jekyll-commonmark (~> 1.4.0)
rouge (>= 2.0, < 4.0)
jekyll-default-layout (0.1.4)
jekyll (~> 3.0)
jekyll-feed (0.15.1)
rouge (>= 2.0, < 5.0)
jekyll-default-layout (0.1.5)
jekyll (>= 3.0, < 5.0)
jekyll-feed (0.17.0)
jekyll (>= 3.7, < 5.0)
jekyll-gist (1.5.0)
octokit (~> 4.2)
jekyll-github-metadata (2.13.0)
jekyll-github-metadata (2.16.1)
jekyll (>= 3.4, < 5.0)
octokit (~> 4.0, != 4.4.0)
octokit (>= 4, < 7, != 4.4.0)
jekyll-include-cache (0.2.1)
jekyll (>= 3.7, < 5.0)
jekyll-mentions (1.6.0)
@ -212,76 +214,71 @@ GEM
jekyll (>= 3.3, < 5.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
jemoji (0.12.0)
gemoji (~> 3.0)
jemoji (0.13.0)
gemoji (>= 3, < 5)
html-pipeline (~> 2.2)
jekyll (>= 3.0, < 5.0)
kramdown (2.3.2)
json (2.13.1)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.3)
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
liquid (4.0.4)
listen (3.9.0)
rb-inotify (~> 0.9, >= 0.9.10)
logger (1.6.6)
mercenary (0.3.6)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.17.0)
multipart-post (2.1.1)
nokogiri (1.16.2-arm64-darwin)
minitest (5.25.5)
net-http (0.6.0)
uri
nokogiri (1.18.9-arm64-darwin)
racc (~> 1.4)
nokogiri (1.16.2-x86_64-linux)
nokogiri (1.18.9-x86_64-linux-gnu)
racc (~> 1.4)
octokit (4.22.0)
faraday (>= 0.9)
sawyer (~> 0.8.0, >= 0.5.3)
octokit (4.25.1)
faraday (>= 1, < 3)
sawyer (~> 0.9)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (4.0.7)
racc (1.7.3)
rb-fsevent (0.11.1)
rb-inotify (0.10.1)
public_suffix (5.1.1)
racc (1.8.1)
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
ffi (~> 1.0)
rexml (3.3.2)
strscan
rouge (3.26.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
rexml (3.4.1)
rouge (3.30.0)
rubyzip (2.4.1)
safe_yaml (1.0.5)
sass (3.7.4)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sawyer (0.8.2)
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (> 0.8, < 2.0)
simpleidn (0.2.1)
unf (~> 0.1.4)
strscan (3.1.0)
faraday (>= 0.17.3, < 3)
securerandom (0.4.1)
simpleidn (0.2.3)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thread_safe (0.3.6)
typhoeus (1.4.0)
typhoeus (1.4.1)
ethon (>= 0.9.0)
tzinfo (1.2.11)
thread_safe (~> 0.1)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.1)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (1.8.0)
zeitwerk (2.6.6)
uri (1.0.3)
webrick (1.9.1)
PLATFORMS
arm64-darwin-21
x86_64-linux
DEPENDENCIES
github-pages (~> 226)
jekyll-feed (~> 0.15.1)
github-pages (~> 232)
BUNDLED WITH
2.3.7

View file

@ -101,7 +101,7 @@ If successfully sent, the API will return the following response:
## GET `/_synapse/admin/v1/register`
Shared secret registration — please see the [user creation page](createusers) for
Shared secret registration — please see the [user creation page](1_createusers.md) for
guidance on configuring and using this endpoint.
## GET `/_matrix/client/v3/admin/whois/{userId}`

85
go.mod
View file

@ -2,12 +2,12 @@ module github.com/element-hq/dendrite
require (
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d
github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/MFAshby/stdemuxerhook v1.0.0
github.com/Masterminds/semver/v3 v3.3.1
github.com/blevesearch/bleve/v2 v2.4.4
github.com/blevesearch/bleve/v2 v2.5.2
github.com/codeclysm/extract v2.2.0+incompatible
github.com/coder/websocket v1.8.12
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
@ -17,7 +17,7 @@ require (
github.com/foxcpp/go-mockdns v1.1.0
github.com/getsentry/sentry-go v0.14.0
github.com/gologme/log v1.3.0
github.com/google/go-cmp v0.6.0
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.5.3
@ -26,17 +26,17 @@ 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-20250116181547-c4f1e01eab0d
github.com/matrix-org/gomatrixserverlib v0.0.0-20250815065806-6697d93cbcba
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.24
github.com/nats-io/nats-server/v2 v2.10.25
github.com/nats-io/nats.go v1.38.0
github.com/mattn/go-sqlite3 v1.14.28
github.com/nats-io/nats-server/v2 v2.11.7
github.com/nats-io/nats.go v1.44.0
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/opentracing/opentracing-go v1.2.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.20.5
github.com/prometheus/client_golang v1.22.0
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0
github.com/tidwall/gjson v1.18.0
@ -46,15 +46,15 @@ require (
github.com/yggdrasil-network/yggdrasil-go v0.5.12
github.com/yggdrasil-network/yggquic v0.0.0-20241212194307-0d495106021f
go.uber.org/atomic v1.11.0
golang.org/x/crypto v0.32.0
golang.org/x/crypto v0.40.0
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
golang.org/x/image v0.23.0
golang.org/x/image v0.27.0
golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b
golang.org/x/net v0.33.0
golang.org/x/sync v0.10.0
golang.org/x/term v0.28.0
golang.org/x/sync v0.16.0
golang.org/x/term v0.33.0
gopkg.in/yaml.v2 v2.4.0
gotest.tools/v3 v3.5.1
gotest.tools/v3 v3.5.2
maunium.net/go/mautrix v0.15.1
modernc.org/sqlite v1.34.5
)
@ -63,27 +63,27 @@ require (
github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3 // indirect
github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/RoaringBitmap/roaring v1.9.3 // indirect
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect
github.com/bits-and-blooms/bitset v1.22.0 // indirect
github.com/bits-and-blooms/bloom/v3 v3.7.0 // indirect
github.com/blevesearch/bleve_index_api v1.1.12 // indirect
github.com/blevesearch/geo v0.1.20 // indirect
github.com/blevesearch/go-faiss v1.0.24 // indirect
github.com/blevesearch/bleve_index_api v1.2.8 // indirect
github.com/blevesearch/geo v0.2.3 // indirect
github.com/blevesearch/go-faiss v1.0.25 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.2.16 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.3.10 // indirect
github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
github.com/blevesearch/vellum v1.0.10 // indirect
github.com/blevesearch/zapx/v11 v11.3.10 // indirect
github.com/blevesearch/zapx/v12 v12.3.10 // indirect
github.com/blevesearch/zapx/v13 v13.3.10 // indirect
github.com/blevesearch/zapx/v14 v14.3.10 // indirect
github.com/blevesearch/zapx/v15 v15.3.16 // indirect
github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b // indirect
github.com/blevesearch/vellum v1.1.0 // indirect
github.com/blevesearch/zapx/v11 v11.4.2 // indirect
github.com/blevesearch/zapx/v12 v12.4.2 // indirect
github.com/blevesearch/zapx/v13 v13.4.2 // indirect
github.com/blevesearch/zapx/v14 v14.4.2 // indirect
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/log v0.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
@ -97,15 +97,16 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-tpm v0.9.5 // indirect
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
github.com/h2non/filetype v1.1.3 // indirect
github.com/hashicorp/go-set/v3 v3.0.0 // indirect
github.com/hjson/hjson-go/v4 v4.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/juju/errors v1.0.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/miekg/dns v1.1.57 // indirect
@ -117,16 +118,17 @@ require (
github.com/morikuni/aec v1.0.0 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nats-io/jwt/v2 v2.7.3 // indirect
github.com/nats-io/nkeys v0.4.9 // indirect
github.com/nats-io/jwt/v2 v2.7.4 // indirect
github.com/nats-io/nkeys v0.4.11 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/oleiade/lane/v2 v2.0.0 // indirect
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/quic-go/quic-go v0.48.2 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
@ -135,7 +137,7 @@ require (
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/wlynxg/anet v0.0.5 // indirect
go.etcd.io/bbolt v1.3.7 // indirect
go.etcd.io/bbolt v1.4.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
go.opentelemetry.io/otel v1.32.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 // indirect
@ -143,12 +145,13 @@ require (
go.opentelemetry.io/otel/sdk v1.32.0 // indirect
go.opentelemetry.io/otel/trace v1.32.0 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.23.0 // indirect
google.golang.org/protobuf v1.35.1 // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.34.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/macaroon.v2 v2.1.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
maunium.net/go/maulogger/v2 v2.4.1 // indirect
@ -158,6 +161,6 @@ require (
nhooyr.io/websocket v1.8.7 // indirect
)
go 1.22
go 1.23.0
toolchain go1.23.2
toolchain go1.24.3

177
go.sum
View file

@ -6,8 +6,8 @@ github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/MFAshby/stdemuxerhook v1.0.0 h1:1XFGzakrsHMv76AeanPDL26NOgwjPl/OUxbGhJthwMc=
@ -16,61 +16,63 @@ github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7r
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM=
github.com/RoaringBitmap/roaring v1.9.3/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg=
github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/antithesishq/antithesis-sdk-go v0.4.3-default-no-op h1:+OSa/t11TFhqfrX0EOSqQBDJ0YlpmK0rDSiB19dg9M0=
github.com/antithesishq/antithesis-sdk-go v0.4.3-default-no-op/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl3v2yvUZjmKncl7U91fup7E=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4=
github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bloom/v3 v3.7.0 h1:VfknkqV4xI+PsaDIsoHueyxVDZrfvMn56jeWUzvzdls=
github.com/bits-and-blooms/bloom/v3 v3.7.0/go.mod h1:VKlUSvp0lFIYqxJjzdnSsZEw4iHb1kOL2tfHTgyJBHg=
github.com/blevesearch/bleve/v2 v2.4.4 h1:RwwLGjUm54SwyyykbrZs4vc1qjzYic4ZnAnY9TwNl60=
github.com/blevesearch/bleve/v2 v2.4.4/go.mod h1:fa2Eo6DP7JR+dMFpQe+WiZXINKSunh7WBtlDGbolKXk=
github.com/blevesearch/bleve_index_api v1.1.12 h1:P4bw9/G/5rulOF7SJ9l4FsDoo7UFJ+5kexNy1RXfegY=
github.com/blevesearch/bleve_index_api v1.1.12/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8=
github.com/blevesearch/geo v0.1.20 h1:paaSpu2Ewh/tn5DKn/FB5SzvH0EWupxHEIwbCk/QPqM=
github.com/blevesearch/geo v0.1.20/go.mod h1:DVG2QjwHNMFmjo+ZgzrIq2sfCh6rIHzy9d9d0B59I6w=
github.com/blevesearch/go-faiss v1.0.24 h1:K79IvKjoKHdi7FdiXEsAhxpMuns0x4fM0BO93bW5jLI=
github.com/blevesearch/go-faiss v1.0.24/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/bleve/v2 v2.5.2 h1:Ab0r0MODV2C5A6BEL87GqLBySqp/s9xFgceCju6BQk8=
github.com/blevesearch/bleve/v2 v2.5.2/go.mod h1:5Dj6dUQxZM6aqYT3eutTD/GpWKGFSsV8f7LDidFbwXo=
github.com/blevesearch/bleve_index_api v1.2.8 h1:Y98Pu5/MdlkRyLM0qDHostYo7i+Vv1cDNhqTeR4Sy6Y=
github.com/blevesearch/bleve_index_api v1.2.8/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
github.com/blevesearch/geo v0.2.3 h1:K9/vbGI9ehlXdxjxDRJtoAMt7zGAsMIzc6n8zWcwnhg=
github.com/blevesearch/geo v0.2.3/go.mod h1:K56Q33AzXt2YExVHGObtmRSFYZKYGv0JEN5mdacJJR8=
github.com/blevesearch/go-faiss v1.0.25 h1:lel1rkOUGbT1CJ0YgzKwC7k+XH0XVBHnCVWahdCXk4U=
github.com/blevesearch/go-faiss v1.0.25/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk=
github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
github.com/blevesearch/scorch_segment_api/v2 v2.2.16 h1:uGvKVvG7zvSxCwcm4/ehBa9cCEuZVE+/zvrSl57QUVY=
github.com/blevesearch/scorch_segment_api/v2 v2.2.16/go.mod h1:VF5oHVbIFTu+znY1v30GjSpT5+9YFs9dV2hjvuh34F0=
github.com/blevesearch/scorch_segment_api/v2 v2.3.10 h1:Yqk0XD1mE0fDZAJXTjawJ8If/85JxnLd8v5vG/jWE/s=
github.com/blevesearch/scorch_segment_api/v2 v2.3.10/go.mod h1:Z3e6ChN3qyN35yaQpl00MfI5s8AxUJbpTR/DL8QOQ+8=
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw=
github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s=
github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs=
github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A=
github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ=
github.com/blevesearch/vellum v1.0.10 h1:HGPJDT2bTva12hrHepVT3rOyIKFFF4t7Gf6yMxyMIPI=
github.com/blevesearch/vellum v1.0.10/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k=
github.com/blevesearch/zapx/v11 v11.3.10 h1:hvjgj9tZ9DeIqBCxKhi70TtSZYMdcFn7gDb71Xo/fvk=
github.com/blevesearch/zapx/v11 v11.3.10/go.mod h1:0+gW+FaE48fNxoVtMY5ugtNHHof/PxCqh7CnhYdnMzQ=
github.com/blevesearch/zapx/v12 v12.3.10 h1:yHfj3vXLSYmmsBleJFROXuO08mS3L1qDCdDK81jDl8s=
github.com/blevesearch/zapx/v12 v12.3.10/go.mod h1:0yeZg6JhaGxITlsS5co73aqPtM04+ycnI6D1v0mhbCs=
github.com/blevesearch/zapx/v13 v13.3.10 h1:0KY9tuxg06rXxOZHg3DwPJBjniSlqEgVpxIqMGahDE8=
github.com/blevesearch/zapx/v13 v13.3.10/go.mod h1:w2wjSDQ/WBVeEIvP0fvMJZAzDwqwIEzVPnCPrz93yAk=
github.com/blevesearch/zapx/v14 v14.3.10 h1:SG6xlsL+W6YjhX5N3aEiL/2tcWh3DO75Bnz77pSwwKU=
github.com/blevesearch/zapx/v14 v14.3.10/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns=
github.com/blevesearch/zapx/v15 v15.3.16 h1:Ct3rv7FUJPfPk99TI/OofdC+Kpb4IdyfdMH48sb+FmE=
github.com/blevesearch/zapx/v15 v15.3.16/go.mod h1:Turk/TNRKj9es7ZpKK95PS7f6D44Y7fAFy8F4LXQtGg=
github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b h1:ju9Az5YgrzCeK3M1QwvZIpxYhChkXp7/L0RhDYsxXoE=
github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b/go.mod h1:BlrYNpOu4BvVRslmIG+rLtKhmjIaRhIbG8sb9scGTwI=
github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w=
github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y=
github.com/blevesearch/zapx/v11 v11.4.2 h1:l46SV+b0gFN+Rw3wUI1YdMWdSAVhskYuvxlcgpQFljs=
github.com/blevesearch/zapx/v11 v11.4.2/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc=
github.com/blevesearch/zapx/v12 v12.4.2 h1:fzRbhllQmEMUuAQ7zBuMvKRlcPA5ESTgWlDEoB9uQNE=
github.com/blevesearch/zapx/v12 v12.4.2/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58=
github.com/blevesearch/zapx/v13 v13.4.2 h1:46PIZCO/ZuKZYgxI8Y7lOJqX3Irkc3N8W82QTK3MVks=
github.com/blevesearch/zapx/v13 v13.4.2/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk=
github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT7fWYz0=
github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8=
github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k=
github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw=
github.com/blevesearch/zapx/v16 v16.2.4 h1:tGgfvleXTAkwsD5mEzgM3zCS/7pgocTCnO1oyAUjlww=
github.com/blevesearch/zapx/v16 v16.2.4/go.mod h1:Rti/REtuuMmzwsI8/C/qIzRaEoSK/wiFYw5e5ctUKKs=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/codeclysm/extract v2.2.0+incompatible h1:q3wyckoA30bhUSiwdQezMqVhwd8+WGE64/GL//LtUhI=
github.com/codeclysm/extract v2.2.0+incompatible/go.mod h1:2nhFMPHiU9At61hz+12bfrlpXSUrOnK+wR+KlGO4Uks=
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
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/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=
@ -109,8 +111,8 @@ github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/
github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI=
github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk=
github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
github.com/getlantern/fdcount v0.0.0-20210503151800-5decd65b3731/go.mod h1:XZwE+iIlAgr64OFbXKFNCllBwV4wEipPx8Hlo2gZdbM=
@ -166,8 +168,6 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
@ -181,8 +181,10 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-tpm v0.9.5 h1:ocUmnDebX54dnW+MQWGQRbdaAcJELsa6PqZhJ48KwVU=
github.com/google/go-tpm v0.9.5/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
@ -199,6 +201,8 @@ github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/go-set/v3 v3.0.0 h1:CaJBQvQCOWoftrBcDt7Nwgo0kdpmrKxar/x2o6pV9JA=
github.com/hashicorp/go-set/v3 v3.0.0/go.mod h1:IEghM2MpE5IaNvL+D7X480dfNtxjRXZ6VMpK3C8s2ok=
github.com/hjson/hjson-go/v4 v4.4.0 h1:D/NPvqOCH6/eisTb5/ztuIS8GUvmpHaLOcNk1Bjr298=
github.com/hjson/hjson-go/v4 v4.4.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@ -211,9 +215,10 @@ github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5l
github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@ -234,8 +239,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-20250116181547-c4f1e01eab0d h1:c3Dkci0GDH/6cGGt8zGIiJMP+UOdtX0DPY6dxiJvtZM=
github.com/matrix-org/gomatrixserverlib v0.0.0-20250116181547-c4f1e01eab0d/go.mod h1:qil34SWn6VB6gO5312rzziCUcZtgROPjrLE+4ly/0os=
github.com/matrix-org/gomatrixserverlib v0.0.0-20250815065806-6697d93cbcba h1:vUUjTOXZ/bYdF/SmJPH8HZ/UTmvw+ldngFKVLElmn+I=
github.com/matrix-org/gomatrixserverlib v0.0.0-20250815065806-6697d93cbcba/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=
@ -248,10 +253,11 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q=
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=
@ -270,14 +276,14 @@ github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nats-io/jwt/v2 v2.7.3 h1:6bNPK+FXgBeAqdj4cYQ0F8ViHRbi7woQLq4W29nUAzE=
github.com/nats-io/jwt/v2 v2.7.3/go.mod h1:GvkcbHhKquj3pkioy5put1wvPxs78UlZ7D/pY+BgZk4=
github.com/nats-io/nats-server/v2 v2.10.25 h1:J0GWLDDXo5HId7ti/lTmBfs+lzhmu8RPkoKl0eSCqwc=
github.com/nats-io/nats-server/v2 v2.10.25/go.mod h1:/YYYQO7cuoOBt+A7/8cVjuhWTaTUEAlZbJT+3sMAfFU=
github.com/nats-io/nats.go v1.38.0 h1:A7P+g7Wjp4/NWqDOOP/K6hfhr54DvdDQUznt5JFg9XA=
github.com/nats-io/nats.go v1.38.0/go.mod h1:IGUM++TwokGnXPs82/wCuiHS02/aKrdYUQkU8If6yjw=
github.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0=
github.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE=
github.com/nats-io/jwt/v2 v2.7.4 h1:jXFuDDxs/GQjGDZGhNgH4tXzSUK6WQi2rsj4xmsNOtI=
github.com/nats-io/jwt/v2 v2.7.4/go.mod h1:me11pOkwObtcBNR8AiMrUbtVOUGkqYjMQZ6jnSdVUIA=
github.com/nats-io/nats-server/v2 v2.11.7 h1:lINWQ/Hb3cnaoHmWTjj/7WppZnaSh9C/1cD//nHCbms=
github.com/nats-io/nats-server/v2 v2.11.7/go.mod h1:DchDPVzAsAPqhqm7VLedX0L7hjnV/SYtlmsl9F8U53s=
github.com/nats-io/nats.go v1.44.0 h1:ECKVrDLdh/kDPV1g0gAQ+2+m2KprqZK5O/eJAyAnH2M=
github.com/nats-io/nats.go v1.44.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
@ -285,6 +291,8 @@ github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJm
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oleiade/lane/v2 v2.0.0 h1:XW/ex/Inr+bPkLd3O240xrFOhUkTd4Wy176+Gv0E3Qw=
github.com/oleiade/lane/v2 v2.0.0/go.mod h1:i5FBPFAYSWCgLh58UkUGCChjcCzef/MI7PlQm2TKCeg=
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
@ -306,12 +314,12 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
@ -323,6 +331,8 @@ github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWN
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc=
github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU=
github.com/shoenig/test v1.11.0 h1:NoPa5GIoBwuqzIviCrnUJa+t5Xb4xi5Z+zODJnIDsEQ=
github.com/shoenig/test v1.11.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
@ -367,8 +377,8 @@ github.com/yggdrasil-network/yggquic v0.0.0-20241212194307-0d495106021f/go.mod h
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
@ -399,8 +409,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -411,8 +421,8 @@ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbR
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b h1:WX7nnnLfCEXg+FmdYZPai2XuP3VqCP1HZVMST0n9DF0=
golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b/go.mod h1:EiXZlVfUTaAyySFVJb9rsODuiO+WXu8HrUuySb7nYFw=
@ -423,8 +433,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -437,8 +447,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -447,8 +457,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -472,8 +482,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@ -481,8 +491,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -492,11 +502,11 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -508,8 +518,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -526,8 +536,8 @@ google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
@ -541,10 +551,11 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8=
maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
maunium.net/go/mautrix v0.15.1 h1:pmCtMjYRpd83+2UL+KTRFYQo5to0373yulimvLK+1k0=

View file

@ -37,7 +37,7 @@ type CanonicalAlias struct {
// if they have not been specified.
// http://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-power-levels
// https://github.com/matrix-org/synapse/blob/v0.19.2/synapse/handlers/room.py#L294
func InitialPowerLevelsContent(roomCreator string) (c gomatrixserverlib.PowerLevelContent) {
func InitialPowerLevelsContent(roomVersion gomatrixserverlib.IRoomVersion, roomCreator string) (c gomatrixserverlib.PowerLevelContent) {
c.Defaults()
c.Events = map[string]int64{
"m.room.name": 50,
@ -49,7 +49,12 @@ func InitialPowerLevelsContent(roomCreator string) (c gomatrixserverlib.PowerLev
"m.room.encryption": 100,
"m.room.server_acl": 100,
}
c.Users = map[string]int64{roomCreator: 100}
c.Users = map[string]int64{}
if roomVersion.PrivilegedCreators() {
c.Events["m.room.tombstone"] = 150
} else {
c.Users[roomCreator] = 100
}
return c
}

View file

@ -67,16 +67,24 @@ func BuildEvent(
identity *fclient.SigningIdentity, evTime time.Time,
eventsNeeded *gomatrixserverlib.StateNeeded, queryRes *api.QueryLatestEventsAndStateResponse,
) (*types.HeaderedEvent, error) {
if err := addPrevEventsToEvent(proto, eventsNeeded, queryRes); err != nil {
return nil, err
}
verImpl, err := gomatrixserverlib.GetRoomVersion(queryRes.RoomVersion)
if err != nil {
return nil, err
}
proto.Version = verImpl
if err = addPrevEventsToEvent(proto, eventsNeeded, queryRes); err != nil {
return nil, err
}
builder := verImpl.NewEventBuilderFromProtoEvent(proto)
if verImpl.DomainlessRoomIDs() && builder.RoomID != "" && proto.Type == spec.MRoomCreate && proto.StateKey != nil && *proto.StateKey == "" {
return nil, gomatrixserverlib.EventValidationError{
Message: "cannot resend m.room.create event",
Code: 400,
}
}
event, err := builder.Build(
evTime, identity.ServerName, identity.KeyID,
identity.PrivateKey,
@ -136,8 +144,22 @@ func addPrevEventsToEvent(
if err != nil {
return fmt.Errorf("eventsNeeded.AuthEventReferences: %w", err)
}
var authEventIDs []string
if builder.Version.DomainlessRoomIDs() && len(builder.RoomID) > 0 {
// the room ID is the create event so we shouldn't set it in auth_events
authEventIDs = make([]string, 0, len(refs))
createEventID := fmt.Sprintf("$%s", builder.RoomID[1:])
for _, id := range refs {
if id == createEventID {
continue
}
authEventIDs = append(authEventIDs, id)
}
} else {
authEventIDs = refs
}
builder.AuthEvents, builder.PrevEvents = truncateAuthAndPrevEvents(refs, queryRes.LatestEvents)
builder.AuthEvents, builder.PrevEvents = truncateAuthAndPrevEvents(authEventIDs, queryRes.LatestEvents)
return nil
}

View file

@ -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
}

View file

@ -17,8 +17,8 @@ var build string
const (
VersionMajor = 0
VersionMinor = 14
VersionPatch = 1
VersionMinor = 15
VersionPatch = 2
VersionTag = "" // example: "rc1"
gitRevLen = 7 // 7 matches the displayed characters on github.com

View file

@ -243,7 +243,7 @@ type ClientRoomserverAPI interface {
PerformCreateRoom(ctx context.Context, userID spec.UserID, roomID spec.RoomID, createRequest *PerformCreateRoomRequest) (string, *util.JSONResponse)
// PerformRoomUpgrade upgrades a room to a newer version
PerformRoomUpgrade(ctx context.Context, roomID string, userID spec.UserID, roomVersion gomatrixserverlib.RoomVersion) (newRoomID string, err error)
PerformRoomUpgrade(ctx context.Context, roomID string, userID spec.UserID, roomVersion gomatrixserverlib.RoomVersion, additionalCreators []string) (newRoomID string, err error)
PerformAdminEvacuateRoom(ctx context.Context, roomID string) (affected []string, err error)
PerformAdminEvacuateUser(ctx context.Context, userID string) (affected []string, err error)
PerformAdminPurgeRoom(ctx context.Context, roomID string) error

View file

@ -15,6 +15,9 @@ import (
"github.com/matrix-org/gomatrixserverlib/spec"
)
// for detecting rejected events and returning 403 instead of 500ing
const InputWasRejected = "InputWasRejected"
type Kind int
const (
@ -108,5 +111,5 @@ func (r *InputRoomEventsResponse) Err() error {
Message: r.ErrMsg,
}
}
return fmt.Errorf("InputRoomEventsResponse: %s", r.ErrMsg)
return fmt.Errorf(r.ErrMsg)
}

View file

@ -17,6 +17,7 @@ type PerformCreateRoomRequest struct {
Topic string
StatePreset string
CreationContent json.RawMessage
CreateEvent json.RawMessage
InitialState []gomatrixserverlib.FledglingEvent
RoomAliasName string
RoomVersion gomatrixserverlib.RoomVersion

View file

@ -8,6 +8,10 @@ package api
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/element-hq/dendrite/roomserver/types"
"github.com/matrix-org/gomatrixserverlib"
@ -216,3 +220,117 @@ func PopulatePublicRooms(ctx context.Context, roomIDs []string, rsAPI QueryBulkS
}
return chunk, nil
}
func GenerateCreateContent(ctx context.Context, roomVer gomatrixserverlib.RoomVersion, senderID string, createContentJSON json.RawMessage, additionalCreators []string) (map[string]any, error) {
createContent := map[string]any{}
if len(createContentJSON) > 0 {
if err := json.Unmarshal(createContentJSON, &createContent); err != nil {
return nil, fmt.Errorf("invalid create content: %s", err)
}
}
// TODO: Maybe, at some point, GMSL should return the events to create, so we can define the version
// entirely there.
switch roomVer {
case gomatrixserverlib.RoomVersionV11:
fallthrough
case gomatrixserverlib.RoomVersionV12:
// RoomVersionV11 removed the creator field from the create content: https://github.com/matrix-org/matrix-spec-proposals/pull/2175
default:
createContent["creator"] = senderID
}
createContent["room_version"] = string(roomVer)
verImpl := gomatrixserverlib.MustGetRoomVersion(roomVer)
if verImpl.PrivilegedCreators() {
var finalAdditionalCreators []string
creatorsSet := make(map[string]struct{})
var unverifiedCreators []string
unverifiedCreators = append(unverifiedCreators, additionalCreators...)
// they get added to any additional creators specified already
existingAdditionalCreators, ok := createContent["additional_creators"].([]any)
if ok {
for _, add := range existingAdditionalCreators {
addStr, ok := add.(string)
if ok {
unverifiedCreators = append(unverifiedCreators, addStr)
}
}
}
for _, add := range unverifiedCreators {
if _, exists := creatorsSet[add]; exists {
continue
}
_, err := spec.NewUserID(add, true)
if err != nil {
return nil, fmt.Errorf("invalid additional creator: '%s': %s", add, err)
}
finalAdditionalCreators = append(finalAdditionalCreators, add)
creatorsSet[add] = struct{}{}
}
if len(finalAdditionalCreators) > 0 {
createContent["additional_creators"] = finalAdditionalCreators
}
}
return createContent, nil
}
func GeneratePDU(
ctx context.Context, verImpl gomatrixserverlib.IRoomVersion, e gomatrixserverlib.FledglingEvent, authEvents *gomatrixserverlib.AuthEvents, depth int, prevEventID string,
identity *fclient.SigningIdentity, timestamp time.Time, senderID, roomID string, queryer QuerySenderIDAPI,
) (gomatrixserverlib.PDU, *util.JSONResponse) {
builder := verImpl.NewEventBuilderFromProtoEvent(&gomatrixserverlib.ProtoEvent{
SenderID: senderID,
RoomID: roomID,
Type: e.Type,
StateKey: &e.StateKey,
Depth: int64(depth),
})
err := builder.SetContent(e.Content)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("builder.SetContent failed")
return nil, &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
if prevEventID != "" {
builder.PrevEvents = []string{prevEventID}
}
var ev gomatrixserverlib.PDU
if err = builder.AddAuthEvents(authEvents); err != nil {
util.GetLogger(ctx).WithError(err).Error("AddAuthEvents failed")
return nil, &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
ev, err = builder.Build(timestamp, identity.ServerName, identity.KeyID, identity.PrivateKey)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("buildEvent failed")
return nil, &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
if err = gomatrixserverlib.Allowed(ev, authEvents, func(roomID spec.RoomID, senderID spec.SenderID) (*spec.UserID, error) {
return queryer.QueryUserIDForSender(ctx, roomID, senderID)
}); err != nil {
util.GetLogger(ctx).WithError(err).Error("gomatrixserverlib.Allowed failed")
validationErr, ok := err.(*gomatrixserverlib.EventValidationError)
if ok {
return nil, &util.JSONResponse{
Code: validationErr.Code,
JSON: spec.Forbidden(err.Error()),
}
}
return nil, &util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden(err.Error()),
}
}
return ev, nil
}

View file

@ -11,6 +11,7 @@ import (
"database/sql"
"errors"
"fmt"
"slices"
"time"
asAPI "github.com/element-hq/dendrite/appservice/api"
@ -134,7 +135,7 @@ func (r *RoomserverInternalAPI) RemoveRoomAlias(ctx context.Context, senderID sp
}
if spec.SenderID(creatorID) != senderID {
var plEvent *types.HeaderedEvent
var createEvent, plEvent *types.HeaderedEvent
var pls *gomatrixserverlib.PowerLevelContent
plEvent, err = r.DB.GetStateEvent(ctx, roomID, spec.MRoomPowerLevels, "")
@ -147,7 +148,14 @@ func (r *RoomserverInternalAPI) RemoveRoomAlias(ctx context.Context, senderID sp
return true, false, fmt.Errorf("plEvent.PowerLevels: %w", err)
}
if pls.UserLevel(senderID) < pls.EventLevel(spec.MRoomCanonicalAlias, true) {
createEvent, err = r.DB.GetStateEvent(ctx, roomID, spec.MRoomCreate, "")
if err != nil {
return true, false, fmt.Errorf("r.DB.GetStateEvent: %w", err)
}
isPrivilegedCreator := gomatrixserverlib.MustGetRoomVersion(createEvent.Version()).PrivilegedCreators() &&
slices.Contains(gomatrixserverlib.CreatorsFromCreateEvent(createEvent), string(senderID))
if !isPrivilegedCreator && pls.UserLevel(senderID) < pls.EventLevel(spec.MRoomCanonicalAlias, true) {
return true, false, nil
}
}

View file

@ -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(),
@ -265,39 +272,61 @@ func (w *worker) _next() {
msgs, err := w.subscription.Fetch(1, nats.Context(ctx))
switch err {
case nil:
// Is the server shutting down? If so, stop processing.
if w.r.ProcessContext.Context().Err() != nil {
return
}
// 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
}
case context.DeadlineExceeded, context.Canceled:
// 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.Lock()
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
// over the entire stream will recreate this anyway.
if err = w.subscription.Unsubscribe(); err != nil {
logrus.WithError(err).Errorf("Failed to unsubscribe to stream for room %q", w.roomID)
}
w.subscription = nil
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
@ -306,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
@ -326,6 +364,7 @@ func (w *worker) _next() {
// a string, because we might want to return that to the caller if
// it was a synchronous request.
var errString string
wasRejected := false
if err = w.r.processRoomEvent(
w.r.ProcessContext.Context(),
spec.ServerName(msg.Header.Get("virtual_host")),
@ -339,6 +378,7 @@ func (w *worker) _next() {
"event_id": inputRoomEvent.Event.EventID(),
"type": inputRoomEvent.Event.Type(),
}).Warn("Roomserver rejected event")
wasRejected = true
default:
if !errors.Is(err, context.DeadlineExceeded) && !errors.Is(err, context.Canceled) {
w.sentryHub.CaptureException(err)
@ -360,6 +400,10 @@ func (w *worker) _next() {
_ = msg.AckSync()
}
if wasRejected {
errString = api.InputWasRejected
}
// If it was a synchronous input request then the "sync" field
// will be present in the message. That means that someone is
// waiting for a response. The temporary inbox name is present in

View file

@ -128,9 +128,9 @@ func (r *Backfiller) backfillViaFederation(ctx context.Context, req *api.Perform
logrus.WithError(err).WithField("room_id", req.RoomID).Infof("backfilled %d events", len(events))
// persist these new events - auth checks have already been done
roomNID, backfilledEventMap := persistEvents(ctx, r.DB, r.Querier, events)
roomNID, storedEvents := persistEvents(ctx, r.DB, r.Querier, events)
for _, ev := range backfilledEventMap {
for _, ev := range storedEvents {
// now add state for these events
stateIDs, ok := requester.eventIDToBeforeStateIDs[ev.EventID()]
if !ok {
@ -591,10 +591,10 @@ func joinEventsFromHistoryVisibility(
return evs, visibility, err
}
func persistEvents(ctx context.Context, db storage.Database, querier api.QuerySenderIDAPI, events []gomatrixserverlib.PDU) (types.RoomNID, map[string]types.Event) {
func persistEvents(ctx context.Context, db storage.Database, querier api.QuerySenderIDAPI, events []gomatrixserverlib.PDU) (types.RoomNID, []types.Event) {
var roomNID types.RoomNID
var eventNID types.EventNID
backfilledEventMap := make(map[string]types.Event)
storedEvents := make([]types.Event, 0, len(events))
for j, ev := range events {
nidMap, err := db.EventNIDs(ctx, ev.AuthEventIDs())
if err != nil { // this shouldn't happen as RequestBackfill already found them
@ -647,10 +647,10 @@ func persistEvents(ctx context.Context, db storage.Database, querier api.QuerySe
ev = redactedEvent
events[j] = ev
}
backfilledEventMap[ev.EventID()] = types.Event{
storedEvents = append(storedEvents, types.Event{
EventNID: eventNID,
PDU: ev,
}
})
}
return roomNID, backfilledEventMap
return roomNID, storedEvents
}

View file

@ -39,6 +39,7 @@ type Creator struct {
// PerformCreateRoom handles all the steps necessary to create a new room.
// nolint: gocyclo
func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roomID spec.RoomID, createRequest *api.PerformCreateRoomRequest) (string, *util.JSONResponse) {
// Make sure we know the room version
verImpl, err := gomatrixserverlib.GetRoomVersion(createRequest.RoomVersion)
if err != nil {
return "", &util.JSONResponse{
@ -47,17 +48,7 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
}
}
createContent := map[string]interface{}{}
if len(createRequest.CreationContent) > 0 {
if err = json.Unmarshal(createRequest.CreationContent, &createContent); err != nil {
util.GetLogger(ctx).WithError(err).Error("json.Unmarshal for creation_content failed")
return "", &util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("invalid create content"),
}
}
}
// Allocate the room
_, err = c.DB.AssignRoomNID(ctx, roomID, createRequest.RoomVersion)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("failed to assign roomNID")
@ -67,6 +58,7 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
}
}
// Allocate the user
var senderID spec.SenderID
if createRequest.RoomVersion == gomatrixserverlib.RoomVersionPseudoIDs {
// create user room key if needed
@ -83,17 +75,73 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
senderID = spec.SenderID(userID.String())
}
// TODO: Maybe, at some point, GMSL should return the events to create, so we can define the version
// entirely there.
switch createRequest.RoomVersion {
case gomatrixserverlib.RoomVersionV11:
// RoomVersionV11 removed the creator field from the create content: https://github.com/matrix-org/matrix-spec-proposals/pull/2175
default:
createContent["creator"] = senderID
// get the signing identity
identity, err := c.Cfg.Matrix.SigningIdentityFor(userID.Domain()) // we MUST use the server signing mxid_mapping
if err != nil {
logrus.WithError(err).WithField("domain", userID.Domain()).Error("unable to find signing identity for domain")
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
createContent["room_version"] = createRequest.RoomVersion
powerLevelContent := eventutil.InitialPowerLevelsContent(string(senderID))
// Make the create event if we need to
var (
createEvent gomatrixserverlib.PDU
jsonErr *util.JSONResponse
)
authEvents, _ := gomatrixserverlib.NewAuthEvents(nil)
if createRequest.CreateEvent != nil {
createEvent, err = verImpl.NewEventFromTrustedJSON(createRequest.CreateEvent, false)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("gomatrixserverlib.NewEventFromTrustedJSON failed to verify create event")
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
if err = authEvents.AddEvent(createEvent); err != nil {
util.GetLogger(ctx).WithError(err).Error("gomatrixserverlib.AuthEvents.AddEvent failed")
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
} else {
var additionalCreators []string
if createRequest.StatePreset == spec.PresetTrustedPrivateChat {
additionalCreators = createRequest.InvitedUsers
}
createContent, contentErr := api.GenerateCreateContent(ctx, createRequest.RoomVersion, string(senderID), createRequest.CreationContent, additionalCreators)
if contentErr != nil {
util.GetLogger(ctx).WithError(contentErr).Error("GenerateCreateContent failed")
return "", &util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("invalid create content"),
}
}
createEvent, jsonErr = api.GeneratePDU(
ctx, verImpl,
gomatrixserverlib.FledglingEvent{
Type: spec.MRoomCreate,
Content: createContent,
},
authEvents, 1, "", identity, createRequest.EventTime, string(senderID), roomID.String(), c.RSAPI,
)
if jsonErr != nil {
util.GetLogger(ctx).WithError(err).Error("Failed to make the create event")
return "", jsonErr
}
if err = authEvents.AddEvent(createEvent); err != nil {
util.GetLogger(ctx).WithError(err).Error("authEvents.AddEvent failed")
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
}
powerLevelContent := eventutil.InitialPowerLevelsContent(verImpl, string(senderID))
joinRuleContent := gomatrixserverlib.JoinRuleContent{
JoinRule: spec.Invite,
}
@ -122,8 +170,10 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
case spec.PresetTrustedPrivateChat:
joinRuleContent.JoinRule = spec.Invite
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
for _, invitee := range createRequest.InvitedUsers {
powerLevelContent.Users[invitee] = 100
if !verImpl.PrivilegedCreators() {
for _, invitee := range createRequest.InvitedUsers {
powerLevelContent.Users[invitee] = 100
}
}
guestsCanJoin = true
case spec.PresetPublicChat:
@ -131,10 +181,6 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
}
createEvent := gomatrixserverlib.FledglingEvent{
Type: spec.MRoomCreate,
Content: createContent,
}
powerLevelEvent := gomatrixserverlib.FledglingEvent{
Type: spec.MRoomPowerLevels,
Content: powerLevelContent,
@ -158,16 +204,6 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
AvatarURL: createRequest.UserAvatarURL,
}
// get the signing identity
identity, err := c.Cfg.Matrix.SigningIdentityFor(userID.Domain()) // we MUST use the server signing mxid_mapping
if err != nil {
logrus.WithError(err).WithField("domain", userID.Domain()).Error("unable to find signing identity for domain")
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
// If we are creating a room with pseudo IDs, create and sign the MXIDMapping
if createRequest.RoomVersion == gomatrixserverlib.RoomVersionPseudoIDs {
var pseudoIDKey ed25519.PrivateKey
@ -279,7 +315,6 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
switch createRequest.InitialState[i].Type {
case spec.MRoomCreate:
continue
case spec.MRoomPowerLevels:
powerLevelEvent = createRequest.InitialState[i]
@ -321,7 +356,8 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
// harder to reason about, hence sticking to a strict static ordering.
// TODO: Synapse has txn/token ID on each event. Do we need to do this here?
eventsToMake := []gomatrixserverlib.FledglingEvent{
createEvent, membershipEvent, powerLevelEvent, joinRuleEvent, historyVisibilityEvent,
// we made the create event already hence it isn't here.
membershipEvent, powerLevelEvent, joinRuleEvent, historyVisibilityEvent,
}
if guestAccessEvent != nil {
eventsToMake = append(eventsToMake, *guestAccessEvent)
@ -342,61 +378,19 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
// TODO: invite events
// TODO: 3pid invite events
var builtEvents []*types.HeaderedEvent
authEvents, _ := gomatrixserverlib.NewAuthEvents(nil)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("rsapi.QuerySenderIDForUser failed")
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
builtEvents := []*types.HeaderedEvent{
{PDU: createEvent},
}
for i, e := range eventsToMake {
depth := i + 1 // depth starts at 1
depth := i + 2 // depth starts at 2 since we made the create event already
builder := verImpl.NewEventBuilderFromProtoEvent(&gomatrixserverlib.ProtoEvent{
SenderID: string(senderID),
RoomID: roomID.String(),
Type: e.Type,
StateKey: &e.StateKey,
Depth: int64(depth),
})
err = builder.SetContent(e.Content)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("builder.SetContent failed")
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
if i > 0 {
builder.PrevEvents = []string{builtEvents[i-1].EventID()}
}
var ev gomatrixserverlib.PDU
if err = builder.AddAuthEvents(authEvents); err != nil {
util.GetLogger(ctx).WithError(err).Error("AddAuthEvents failed")
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
ev, err = builder.Build(createRequest.EventTime, identity.ServerName, identity.KeyID, identity.PrivateKey)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("buildEvent failed")
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
if err = gomatrixserverlib.Allowed(ev, authEvents, func(roomID spec.RoomID, senderID spec.SenderID) (*spec.UserID, error) {
return c.RSAPI.QueryUserIDForSender(ctx, roomID, senderID)
}); err != nil {
util.GetLogger(ctx).WithError(err).Error("gomatrixserverlib.Allowed failed")
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
ev, jsonErr := api.GeneratePDU(
ctx, verImpl, e,
authEvents, depth, builtEvents[len(builtEvents)-1].EventID(),
identity, createRequest.EventTime, string(senderID), roomID.String(), c.RSAPI,
)
if jsonErr != nil {
return "", jsonErr
}
// Add the event to the list of auth events

View file

@ -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) {

View file

@ -156,19 +156,11 @@ func (r *Joiner) performJoinRoomByID(
}
}
// Get the domain part of the room ID.
roomID, err := spec.NewRoomID(req.RoomIDOrAlias)
if err != nil {
return "", "", rsAPI.ErrInvalidID{Err: fmt.Errorf("room ID %q is invalid: %w", req.RoomIDOrAlias, err)}
}
// If the server name in the room ID isn't ours then it's a
// possible candidate for finding the room via federation. Add
// it to the list of servers to try.
if !r.Cfg.Matrix.IsLocalServerName(roomID.Domain()) {
req.ServerNames = append(req.ServerNames, roomID.Domain())
}
// Force a federated join if we aren't in the room and we've been
// given some server names to try joining by.
inRoomReq := &rsAPI.QueryServerJoinedToRoomRequest{

View file

@ -10,6 +10,7 @@ import (
"context"
"encoding/json"
"fmt"
"slices"
"time"
"github.com/element-hq/dendrite/internal/eventutil"
@ -30,14 +31,15 @@ type Upgrader struct {
// PerformRoomUpgrade upgrades a room from one version to another
func (r *Upgrader) PerformRoomUpgrade(
ctx context.Context,
roomID string, userID spec.UserID, roomVersion gomatrixserverlib.RoomVersion,
roomID string, userID spec.UserID, roomVersion gomatrixserverlib.RoomVersion, additionalCreators []string,
) (newRoomID string, err error) {
return r.performRoomUpgrade(ctx, roomID, userID, roomVersion)
return r.performRoomUpgrade(ctx, roomID, userID, roomVersion, additionalCreators)
}
// nolint:gocyclo
func (r *Upgrader) performRoomUpgrade(
ctx context.Context,
roomID string, userID spec.UserID, roomVersion gomatrixserverlib.RoomVersion,
roomID string, userID spec.UserID, roomVersion gomatrixserverlib.RoomVersion, additionalCreators []string,
) (string, error) {
evTime := time.Now()
@ -64,35 +66,110 @@ func (r *Upgrader) performRoomUpgrade(
return "", api.ErrNotAllowed{Err: fmt.Errorf("You don't have permission to upgrade the room, power level too low.")}
}
// TODO (#267): Check room ID doesn't clash with an existing one, and we
// probably shouldn't be using pseudo-random strings, maybe GUIDs?
newRoomID := fmt.Sprintf("!%s:%s", util.RandomString(16), userID.Domain())
// Get the existing room state for the old room.
oldRoomReq := &api.QueryLatestEventsAndStateRequest{
RoomID: roomID,
}
oldRoomRes := &api.QueryLatestEventsAndStateResponse{}
if err := r.URSAPI.QueryLatestEventsAndState(ctx, oldRoomReq, oldRoomRes); err != nil {
if err = r.URSAPI.QueryLatestEventsAndState(ctx, oldRoomReq, oldRoomRes); err != nil {
return "", fmt.Errorf("Failed to get latest state: %s", err)
}
// Make the tombstone event
tombstoneEvent, pErr := r.makeTombstoneEvent(ctx, evTime, *senderID, userID.Domain(), roomID, newRoomID)
if pErr != nil {
return "", pErr
var oldCreateEvent *types.HeaderedEvent
for _, ev := range oldRoomRes.StateEvents {
if ev.Type() == spec.MRoomCreate && ev.StateKeyEquals("") {
oldCreateEvent = ev
break
}
}
// Make the create event and calculate the new room ID.
var newRoomID string
newRoomVerImpl := gomatrixserverlib.MustGetRoomVersion(roomVersion)
var tombstoneEvent *types.HeaderedEvent
var newCreateEvent gomatrixserverlib.PDU
var pErr error
if !newRoomVerImpl.DomainlessRoomIDs() {
// TODO (#267): Check room ID doesn't clash with an existing one, and we
// probably shouldn't be using pseudo-random strings, maybe GUIDs?
newRoomID = fmt.Sprintf("!%s:%s", util.RandomString(16), userID.Domain())
// Make the tombstone event
tombstoneEvent, pErr = r.makeTombstoneEvent(ctx, evTime, *senderID, userID.Domain(), roomID, newRoomID)
if pErr != nil {
return "", pErr
}
}
content := struct {
Federate *bool `json:"m.federate,omitempty"`
Type string `json:"type,omitempty"`
Predecessor struct {
RoomID string `json:"room_id"`
EventID string `json:"event_id,omitempty"`
} `json:"predecessor"`
}{}
// keep existing values in old room e.g type/m.federate
if err = json.Unmarshal(oldCreateEvent.Content(), &content); err != nil {
return "", fmt.Errorf("failed to copy old create event content to new create event: %s", err)
}
content.Predecessor.RoomID = roomID
content.Predecessor.EventID = ""
if tombstoneEvent != nil {
content.Predecessor.EventID = tombstoneEvent.EventID()
}
contentJSON, err := json.Marshal(content)
if err != nil {
return "", fmt.Errorf("Failed to make content for new create event: %s", err)
}
// make the create event up-front so the roomserver can calculate the room NID to store.
createContent, err := api.GenerateCreateContent(ctx, roomVersion, userID.String(), contentJSON, additionalCreators)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("GenerateCreateContent failed")
return "", fmt.Errorf("failed to GenerateCreateContent")
}
authEvents, _ := gomatrixserverlib.NewAuthEvents(nil)
identity, err := r.Cfg.Matrix.SigningIdentityFor(userID.Domain())
if err != nil {
util.GetLogger(ctx).WithError(err).Error("Failed to get signing identity")
return "", fmt.Errorf("No SigningIdentityFor domain %s", userID.Domain())
}
createEvent, jsonErr := api.GeneratePDU(
ctx, gomatrixserverlib.MustGetRoomVersion(roomVersion),
gomatrixserverlib.FledglingEvent{
Type: spec.MRoomCreate,
Content: createContent,
},
// newRoomID will be empty for domainless rooms
authEvents, 1, "", identity, evTime, userID.String(), newRoomID, r.URSAPI,
)
if jsonErr != nil {
util.GetLogger(ctx).Error("Failed to make the create event")
return "", fmt.Errorf("failed to create new create event PDU")
}
newCreateEvent = createEvent
if newRoomVerImpl.DomainlessRoomIDs() {
newRoomID = newCreateEvent.RoomID().String()
}
if tombstoneEvent == nil {
// Make the tombstone event
tombstoneEvent, pErr = r.makeTombstoneEvent(ctx, evTime, *senderID, userID.Domain(), roomID, newRoomID)
if pErr != nil {
return "", pErr
}
}
creators := gomatrixserverlib.CreatorsFromCreateEvent(newCreateEvent)
// Generate the initial events we need to send into the new room. This includes copied state events and bans
// as well as the power level events needed to set up the room
eventsToMake, pErr := r.generateInitialEvents(ctx, oldRoomRes, *senderID, roomID, roomVersion, tombstoneEvent)
eventsToMake, pErr := r.generateInitialEvents(ctx, oldRoomRes, *senderID, roomID, roomVersion, creators)
if pErr != nil {
return "", pErr
}
// Send the setup events to the new room
if pErr = r.sendInitialEvents(ctx, evTime, *senderID, userID.Domain(), newRoomID, roomVersion, eventsToMake); pErr != nil {
return "", pErr
if pErr = r.sendInitialEvents(ctx, evTime, *senderID, userID.Domain(), newRoomID, roomVersion, newCreateEvent, eventsToMake); pErr != nil {
return "", fmt.Errorf("sendInitialEvents: %s", pErr)
}
// 5. Send the tombstone event to the old room
@ -296,13 +373,25 @@ func (r *Upgrader) userIsAuthorized(ctx context.Context, senderID spec.SenderID,
if err != nil {
return false
}
createEvent := api.GetStateEvent(ctx, r.URSAPI, roomID, gomatrixserverlib.StateKeyTuple{
EventType: spec.MRoomCreate,
StateKey: "",
})
if gomatrixserverlib.MustGetRoomVersion(createEvent.Version()).PrivilegedCreators() &&
slices.Contains(gomatrixserverlib.CreatorsFromCreateEvent(createEvent), string(senderID)) {
return true
}
// Check for power level required to send tombstone event (marks the current room as obsolete),
// if not found, use the StateDefault power level
return pl.UserLevel(senderID) >= pl.EventLevel("m.room.tombstone", true)
}
// Return the events to create AFTER the new create event
// nolint:gocyclo
func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.QueryLatestEventsAndStateResponse, senderID spec.SenderID, roomID string, newVersion gomatrixserverlib.RoomVersion, tombstoneEvent *types.HeaderedEvent) ([]gomatrixserverlib.FledglingEvent, error) {
func (r *Upgrader) generateInitialEvents(
ctx context.Context, oldRoom *api.QueryLatestEventsAndStateResponse, senderID spec.SenderID, _ string, newVersion gomatrixserverlib.RoomVersion,
creators []string) ([]gomatrixserverlib.FledglingEvent, error) {
state := make(map[gomatrixserverlib.StateKeyTuple]*types.HeaderedEvent, len(oldRoom.StateEvents))
for _, event := range oldRoom.StateEvents {
if event.StateKey() == nil {
@ -350,37 +439,10 @@ func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.Query
}
}
oldCreateEvent := state[gomatrixserverlib.StateKeyTuple{EventType: spec.MRoomCreate, StateKey: ""}]
oldMembershipEvent := state[gomatrixserverlib.StateKeyTuple{EventType: spec.MRoomMember, StateKey: string(senderID)}]
oldPowerLevelsEvent := state[gomatrixserverlib.StateKeyTuple{EventType: spec.MRoomPowerLevels, StateKey: ""}]
oldJoinRulesEvent := state[gomatrixserverlib.StateKeyTuple{EventType: spec.MRoomJoinRules, StateKey: ""}]
// Create the new room create event. Using a map here instead of CreateContent
// means that we preserve any other interesting fields that might be present
// in the create event (such as for the room types MSC).
newCreateContent := map[string]interface{}{}
_ = json.Unmarshal(oldCreateEvent.Content(), &newCreateContent)
switch newVersion {
case gomatrixserverlib.RoomVersionV11:
// RoomVersionV11 removed the creator field from the create content: https://github.com/matrix-org/matrix-spec-proposals/pull/2175
// So if we are upgrading from pre v11, we need to remove the field.
delete(newCreateContent, "creator")
default:
newCreateContent["creator"] = senderID
}
newCreateContent["room_version"] = newVersion
newCreateContent["predecessor"] = gomatrixserverlib.PreviousRoom{
EventID: tombstoneEvent.EventID(),
RoomID: roomID,
}
newCreateEvent := gomatrixserverlib.FledglingEvent{
Type: spec.MRoomCreate,
StateKey: "",
Content: newCreateContent,
}
// Now create the new membership event. Same rules apply as above, so
// that we preserve fields we don't otherwise know about. We'll always
// set the membership to join though, because that is necessary to auth
@ -405,7 +467,9 @@ func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.Query
return nil, fmt.Errorf("Power level event content was invalid")
}
tempPowerLevelsEvent, powerLevelsOverridden := createTemporaryPowerLevels(powerLevelContent, senderID)
verImpl := gomatrixserverlib.MustGetRoomVersion(newVersion)
tempPowerLevelsEvent, powerLevelsOverridden := createTemporaryPowerLevels(verImpl, powerLevelContent, senderID, creators)
// Now do the join rules event, same as the create and membership
// events. We'll set a sane default of "invite" so that if the
@ -423,7 +487,7 @@ func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.Query
eventsToMake := make([]gomatrixserverlib.FledglingEvent, 0, len(state))
eventsToMake = append(
eventsToMake, newCreateEvent, newMembershipEvent,
eventsToMake, newMembershipEvent,
tempPowerLevelsEvent, newJoinRulesEvent,
)
@ -467,12 +531,16 @@ func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.Query
return eventsToMake, nil
}
func (r *Upgrader) sendInitialEvents(ctx context.Context, evTime time.Time, senderID spec.SenderID, userDomain spec.ServerName, newRoomID string, newVersion gomatrixserverlib.RoomVersion, eventsToMake []gomatrixserverlib.FledglingEvent) error {
func (r *Upgrader) sendInitialEvents(
ctx context.Context, evTime time.Time, senderID spec.SenderID, userDomain spec.ServerName, newRoomID string,
newVersion gomatrixserverlib.RoomVersion, newCreateEvent gomatrixserverlib.PDU, eventsToMake []gomatrixserverlib.FledglingEvent) error {
var err error
var builtEvents []*types.HeaderedEvent
authEvents, _ := gomatrixserverlib.NewAuthEvents(nil)
builtEvents = append(builtEvents, &types.HeaderedEvent{PDU: newCreateEvent})
authEvents, _ := gomatrixserverlib.NewAuthEvents([]gomatrixserverlib.PDU{newCreateEvent})
for i, e := range eventsToMake {
depth := i + 1 // depth starts at 1
depth := i + 2 // depth starts at 2 since we made the create event already.
proto := gomatrixserverlib.ProtoEvent{
SenderID: string(senderID),
@ -485,9 +553,7 @@ func (r *Upgrader) sendInitialEvents(ctx context.Context, evTime time.Time, send
if err != nil {
return fmt.Errorf("failed to set content of new %q event: %w", proto.Type, err)
}
if i > 0 {
proto.PrevEvents = []string{builtEvents[i-1].EventID()}
}
proto.PrevEvents = []string{builtEvents[i].EventID()}
var verImpl gomatrixserverlib.IRoomVersion
verImpl, err = gomatrixserverlib.GetRoomVersion(newVersion)
@ -503,13 +569,12 @@ func (r *Upgrader) sendInitialEvents(ctx context.Context, evTime time.Time, send
event, err = builder.Build(evTime, userDomain, r.Cfg.Matrix.KeyID, r.Cfg.Matrix.PrivateKey)
if err != nil {
return fmt.Errorf("failed to build new %q event: %w", builder.Type, err)
}
if err = gomatrixserverlib.Allowed(event, authEvents, func(roomID spec.RoomID, senderID spec.SenderID) (*spec.UserID, error) {
return r.URSAPI.QueryUserIDForSender(ctx, roomID, senderID)
}); err != nil {
return fmt.Errorf("Failed to auth new %q event: %w", builder.Type, err)
return fmt.Errorf("Failed to auth new initial %q event: %w", builder.Type, err)
}
// Add the event to the list of auth events
@ -599,7 +664,7 @@ func (r *Upgrader) makeHeaderedEvent(ctx context.Context, evTime time.Time, send
return headeredEvent, nil
}
func createTemporaryPowerLevels(powerLevelContent *gomatrixserverlib.PowerLevelContent, senderID spec.SenderID) (gomatrixserverlib.FledglingEvent, bool) {
func createTemporaryPowerLevels(roomVersion gomatrixserverlib.IRoomVersion, powerLevelContent *gomatrixserverlib.PowerLevelContent, senderID spec.SenderID, creators []string) (gomatrixserverlib.FledglingEvent, bool) {
// Work out what power level we need in order to be able to send events
// of all types into the room.
neededPowerLevel := powerLevelContent.StateDefault
@ -619,14 +684,20 @@ func createTemporaryPowerLevels(powerLevelContent *gomatrixserverlib.PowerLevelC
// so that we can modify them without modifying the original.
tempPowerLevelContent.Users = make(map[string]int64, len(powerLevelContent.Users))
for key, value := range powerLevelContent.Users {
if roomVersion.PrivilegedCreators() && slices.Contains(creators, key) {
continue // don't set the creator in the users map!
}
tempPowerLevelContent.Users[key] = value
}
// If the user who is upgrading the room doesn't already have sufficient
// power, then elevate their power levels.
if tempPowerLevelContent.UserLevel(senderID) < neededPowerLevel {
tempPowerLevelContent.Users[string(senderID)] = neededPowerLevel
powerLevelsOverridden = true
// the upgrader will be the creator so is guaranteed to have enough perms to do this.
if !roomVersion.PrivilegedCreators() {
// If the user who is upgrading the room doesn't already have sufficient
// power, then elevate their power levels.
if tempPowerLevelContent.UserLevel(senderID) < neededPowerLevel {
tempPowerLevelContent.Users[string(senderID)] = neededPowerLevel
powerLevelsOverridden = true
}
}
// Then return the temporary power levels event.

View file

@ -13,7 +13,7 @@ import (
"errors"
"fmt"
//"github.com/element-hq/dendrite/roomserver/internal"
// "github.com/element-hq/dendrite/roomserver/internal"
"github.com/element-hq/dendrite/setup/config"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
@ -747,7 +747,7 @@ func GetAuthChain(
// from the database and the `eventsToFetch` will be updated with any new
// events that we have learned about and need to find. When `eventsToFetch`
// is eventually empty, we should have reached the end of the chain.
eventsToFetch := authEventIDs
eventsToFetch := append([]string{}, authEventIDs...)
authEventsMap := make(map[string]gomatrixserverlib.PDU)
for len(eventsToFetch) > 0 {
@ -779,7 +779,7 @@ func GetAuthChain(
// We've now retrieved all of the events we can. Flatten them down into an
// array and return them.
var authEvents []gomatrixserverlib.PDU
authEvents := make([]gomatrixserverlib.PDU, 0, len(authEventsMap))
for _, event := range authEventsMap {
authEvents = append(authEvents, event)
}

View file

@ -1075,7 +1075,7 @@ func TestUpgrade(t *testing.T) {
if err != nil {
t.Fatalf("upgrade userID is invalid")
}
newRoomID, err := rsAPI.PerformRoomUpgrade(processCtx.Context(), roomID, *userID, rsAPI.DefaultRoomVersion())
newRoomID, err := rsAPI.PerformRoomUpgrade(processCtx.Context(), roomID, *userID, rsAPI.DefaultRoomVersion(), nil)
if err != nil && tc.wantNewRoom {
t.Fatal(err)
}

View file

@ -888,6 +888,8 @@ func (v *StateResolution) resolveConflicts(
case gomatrixserverlib.StateResV1:
return v.resolveConflictsV1(ctx, notConflicted, conflicted)
case gomatrixserverlib.StateResV2:
fallthrough
case gomatrixserverlib.StateResV2_1:
return v.resolveConflictsV2(ctx, notConflicted, conflicted)
}
return nil, fmt.Errorf("unsupported state resolution algorithm %v", stateResAlgo)

View file

@ -1052,6 +1052,7 @@ func (d *EventDatabase) MaybeRedactEvent(
return err
}
// TODO HYDRA: we need to load the create event here
switch {
case powerlevels.UserLevel(redactionEvent.SenderID()) >= powerlevels.Redact:
// 1. The power level of the redaction events sender is greater than or equal to the redact level.

View file

@ -216,14 +216,6 @@ func OnIncomingMessagesRequest(
// TODO: Implement filtering (#587)
// Check the room ID's format.
if _, _, err = gomatrixserverlib.SplitID('!', roomID); err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.MissingParam("Bad room ID: " + err.Error()),
}
}
// If the user already left the room, grep events from before that
if membershipResp.Membership == spec.Leave {
var token types.TopologyToken

View file

@ -92,6 +92,7 @@ func (s *accountDataStatements) SelectAccountDataInRange(
accountDataEventFilter *synctypes.EventFilter,
) (data map[string][]string, pos types.StreamPosition, err error) {
data = make(map[string][]string)
pos = r.Low()
rows, err := sqlutil.TxStmt(txn, s.selectAccountDataInRangeStmt).QueryContext(
ctx, userID, r.Low(), r.High(),
@ -122,7 +123,7 @@ func (s *accountDataStatements) SelectAccountDataInRange(
pos = id
}
}
if pos == 0 {
if len(data) == 0 {
pos = r.High()
}
return data, pos, rows.Err()

View file

@ -84,6 +84,8 @@ func (s *accountDataStatements) SelectAccountDataInRange(
filter *synctypes.EventFilter,
) (data map[string][]string, pos types.StreamPosition, err error) {
data = make(map[string][]string)
pos = r.Low()
stmt, params, err := prepareWithFilters(
s.db, txn, selectAccountDataInRangeSQL,
[]interface{}{
@ -119,7 +121,7 @@ func (s *accountDataStatements) SelectAccountDataInRange(
pos = id
}
}
if pos == 0 {
if len(data) == 0 {
pos = r.High()
}
return data, pos, rows.Err()

View file

@ -46,7 +46,7 @@ func prepareWithFilters(
params, offset = append(params, v), offset+1
}
} else {
query += ` AND sender NOT = ""`
query += ` AND sender != ""`
}
}
if types != nil {
@ -66,7 +66,7 @@ func prepareWithFilters(
params, offset = append(params, v), offset+1
}
} else {
query += ` AND type NOT = ""`
query += ` AND type != ""`
}
}
if containsURL != nil {

View file

@ -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

View file

@ -548,6 +548,13 @@ func testHistoryVisibility(t *testing.T, dbType test.DBType) {
DisplayName: "BOB",
}
filters := []string{
// check that lazy loading doesn't break history visibility
`{"lazy_load_members":true}`,
// Test all kind of filters, including "bad" ones
`{"lazy_load_members":true,"types":null,"not_types":[],"rooms":null,"not_rooms":[],"senders":null,"not_senders":[],"contains_url":null,"io.element.relation_senders":[],"io.element.relation_types":["io.element.thread"]}`,
}
ctx := context.Background()
// check guest and normal user accounts
for _, accType := range []userapi.AccountType{userapi.AccountTypeGuest, userapi.AccountTypeUser} {
@ -614,79 +621,82 @@ func testHistoryVisibility(t *testing.T, dbType test.DBType) {
for _, tc := range testCases {
testname := fmt.Sprintf("%s - %s", tc.historyVisibility, userType)
t.Run(testname, func(t *testing.T) {
// create a room with the given visibility
room := test.NewRoom(t, alice, test.RoomHistoryVisibility(tc.historyVisibility))
for _, filter := range filters {
t.Logf("Using filter: %s", filter)
// create a room with the given visibility
room := test.NewRoom(t, alice, test.RoomHistoryVisibility(tc.historyVisibility))
// send the events/messages to NATS to create the rooms
beforeJoinBody := fmt.Sprintf("Before invite in a %s room", tc.historyVisibility)
beforeJoinEv := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": beforeJoinBody})
eventsToSend := append(room.Events(), beforeJoinEv)
if err := api.SendEvents(ctx, rsAPI, api.KindNew, eventsToSend, "test", "test", "test", nil, false); err != nil {
t.Fatalf("failed to send events: %v", err)
}
syncUntil(t, routers, aliceDev.AccessToken, false,
func(syncBody string) bool {
path := fmt.Sprintf(`rooms.join.%s.timeline.events.#(content.body=="%s")`, room.ID, beforeJoinBody)
return gjson.Get(syncBody, path).Exists()
},
)
// send the events/messages to NATS to create the rooms
beforeJoinBody := fmt.Sprintf("Before invite in a %s room", tc.historyVisibility)
beforeJoinEv := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": beforeJoinBody})
eventsToSend := append(room.Events(), beforeJoinEv)
if err := api.SendEvents(ctx, rsAPI, api.KindNew, eventsToSend, "test", "test", "test", nil, false); err != nil {
t.Fatalf("failed to send events: %v", err)
}
syncUntil(t, routers, aliceDev.AccessToken, false,
func(syncBody string) bool {
path := fmt.Sprintf(`rooms.join.%s.timeline.events.#(content.body=="%s")`, room.ID, beforeJoinBody)
return gjson.Get(syncBody, path).Exists()
},
)
// There is only one event, we expect only to be able to see this, if the room is world_readable
w := httptest.NewRecorder()
routers.Client.ServeHTTP(w, test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/messages", room.ID), test.WithQueryParams(map[string]string{
"access_token": bobDev.AccessToken,
"dir": "b",
"filter": `{"lazy_load_members":true}`, // check that lazy loading doesn't break history visibility
})))
if w.Code != 200 {
t.Logf("%s", w.Body.String())
t.Fatalf("got HTTP %d want %d", w.Code, 200)
}
// We only care about the returned events at this point
var res struct {
Chunk []synctypes.ClientEvent `json:"chunk"`
}
if err := json.NewDecoder(w.Body).Decode(&res); err != nil {
t.Errorf("failed to decode response body: %s", err)
}
// There is only one event, we expect only to be able to see this, if the room is world_readable
w := httptest.NewRecorder()
routers.Client.ServeHTTP(w, test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/messages", room.ID), test.WithQueryParams(map[string]string{
"access_token": bobDev.AccessToken,
"dir": "b",
"filter": filter,
})))
if w.Code != 200 {
t.Logf("%s", w.Body.String())
t.Fatalf("got HTTP %d want %d", w.Code, 200)
}
// We only care about the returned events at this point
var res struct {
Chunk []synctypes.ClientEvent `json:"chunk"`
}
if err := json.NewDecoder(w.Body).Decode(&res); err != nil {
t.Errorf("failed to decode response body: %s", err)
}
verifyEventVisible(t, tc.wantResult.seeWithoutJoin, beforeJoinEv, res.Chunk)
verifyEventVisible(t, tc.wantResult.seeWithoutJoin, beforeJoinEv, res.Chunk)
// Create invite, a message, join the room and create another message.
inviteEv := room.CreateAndInsert(t, alice, "m.room.member", map[string]interface{}{"membership": "invite"}, test.WithStateKey(bob.ID))
afterInviteEv := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": fmt.Sprintf("After invite in a %s room", tc.historyVisibility)})
joinEv := room.CreateAndInsert(t, bob, "m.room.member", map[string]interface{}{"membership": "join"}, test.WithStateKey(bob.ID))
afterJoinBody := fmt.Sprintf("After join in a %s room", tc.historyVisibility)
msgEv := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": afterJoinBody})
// Create invite, a message, join the room and create another message.
inviteEv := room.CreateAndInsert(t, alice, "m.room.member", map[string]interface{}{"membership": "invite"}, test.WithStateKey(bob.ID))
afterInviteEv := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": fmt.Sprintf("After invite in a %s room", tc.historyVisibility)})
joinEv := room.CreateAndInsert(t, bob, "m.room.member", map[string]interface{}{"membership": "join"}, test.WithStateKey(bob.ID))
afterJoinBody := fmt.Sprintf("After join in a %s room", tc.historyVisibility)
msgEv := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": afterJoinBody})
eventsToSend = append([]*rstypes.HeaderedEvent{}, inviteEv, afterInviteEv, joinEv, msgEv)
eventsToSend = append([]*rstypes.HeaderedEvent{}, inviteEv, afterInviteEv, joinEv, msgEv)
if err := api.SendEvents(ctx, rsAPI, api.KindNew, eventsToSend, "test", "test", "test", nil, false); err != nil {
t.Fatalf("failed to send events: %v", err)
if err := api.SendEvents(ctx, rsAPI, api.KindNew, eventsToSend, "test", "test", "test", nil, false); err != nil {
t.Fatalf("failed to send events: %v", err)
}
syncUntil(t, routers, aliceDev.AccessToken, false,
func(syncBody string) bool {
path := fmt.Sprintf(`rooms.join.%s.timeline.events.#(content.body=="%s")`, room.ID, afterJoinBody)
return gjson.Get(syncBody, path).Exists()
},
)
// Verify the messages after/before invite are visible or not
w = httptest.NewRecorder()
routers.Client.ServeHTTP(w, test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/messages", room.ID), test.WithQueryParams(map[string]string{
"access_token": bobDev.AccessToken,
"dir": "b",
})))
if w.Code != 200 {
t.Logf("%s", w.Body.String())
t.Fatalf("got HTTP %d want %d", w.Code, 200)
}
if err := json.NewDecoder(w.Body).Decode(&res); err != nil {
t.Errorf("failed to decode response body: %s", err)
}
// verify results
verifyEventVisible(t, tc.wantResult.seeBeforeJoin, beforeJoinEv, res.Chunk)
verifyEventVisible(t, tc.wantResult.seeAfterInvite, afterInviteEv, res.Chunk)
}
syncUntil(t, routers, aliceDev.AccessToken, false,
func(syncBody string) bool {
path := fmt.Sprintf(`rooms.join.%s.timeline.events.#(content.body=="%s")`, room.ID, afterJoinBody)
return gjson.Get(syncBody, path).Exists()
},
)
// Verify the messages after/before invite are visible or not
w = httptest.NewRecorder()
routers.Client.ServeHTTP(w, test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/messages", room.ID), test.WithQueryParams(map[string]string{
"access_token": bobDev.AccessToken,
"dir": "b",
})))
if w.Code != 200 {
t.Logf("%s", w.Body.String())
t.Fatalf("got HTTP %d want %d", w.Code, 200)
}
if err := json.NewDecoder(w.Body).Decode(&res); err != nil {
t.Errorf("failed to decode response body: %s", err)
}
// verify results
verifyEventVisible(t, tc.wantResult.seeBeforeJoin, beforeJoinEv, res.Chunk)
verifyEventVisible(t, tc.wantResult.seeAfterInvite, afterInviteEv, res.Chunk)
})
}
}

View file

@ -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
/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

View file

@ -94,7 +94,7 @@ func (r *Room) insertCreateEvents(t *testing.T) {
t.Helper()
var joinRule gomatrixserverlib.JoinRuleContent
var hisVis gomatrixserverlib.HistoryVisibilityContent
plContent := eventutil.InitialPowerLevelsContent(r.creator.ID)
plContent := eventutil.InitialPowerLevelsContent(gomatrixserverlib.MustGetRoomVersion(r.Version), r.creator.ID)
switch r.preset {
case PresetTrustedPrivateChat:
fallthrough

View file

@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"slices"
"strings"
"sync"
"time"
@ -604,7 +605,9 @@ func (s *OutputRoomEventConsumer) notifyLocal(ctx context.Context, event *rstype
// ordering guarantees we must provide.
go func() {
// This background processing cannot be tied to a request.
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
// We're generous with the "global" timeout, each HTTP request gets its own context with
// at most 30 seconds below.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
var rejected []*pushgateway.Device
@ -621,7 +624,10 @@ func (s *OutputRoomEventConsumer) notifyLocal(ctx context.Context, event *rstype
// device, rather than per URL. For now, we must
// notify each one separately.
for _, dev := range devices {
rej, err := s.notifyHTTP(ctx, event, url, format, []*pushgateway.Device{dev}, mem.Localpart, roomName, int(userNumUnreadNotifs))
// Give each HTTP request its own context.
httpCtx, httpCancel := context.WithTimeout(ctx, 30*time.Second)
rej, err := s.notifyHTTP(httpCtx, event, url, format, []*pushgateway.Device{dev}, mem.Localpart, roomName, int(userNumUnreadNotifs))
httpCancel()
if err != nil {
log.WithFields(log.Fields{
"event_id": event.EventID(),
@ -722,25 +728,34 @@ func (rse *ruleSetEvalContext) HasPowerLevel(senderID spec.SenderID, levelKey st
req := &rsapi.QueryLatestEventsAndStateRequest{
RoomID: rse.roomID,
StateToFetch: []gomatrixserverlib.StateKeyTuple{
{EventType: spec.MRoomPowerLevels},
{EventType: spec.MRoomPowerLevels, StateKey: ""},
{EventType: spec.MRoomCreate, StateKey: ""},
},
}
var res rsapi.QueryLatestEventsAndStateResponse
if err := rse.rsAPI.QueryLatestEventsAndState(rse.ctx, req, &res); err != nil {
return false, err
}
for _, ev := range res.StateEvents {
if ev.Type() != spec.MRoomPowerLevels {
continue
var createEvent, plEvent *rstypes.HeaderedEvent
for i, ev := range res.StateEvents {
if ev.Type() == spec.MRoomCreate {
createEvent = res.StateEvents[i]
} else if ev.Type() == spec.MRoomPowerLevels {
plEvent = res.StateEvents[i]
}
plc, err := gomatrixserverlib.NewPowerLevelContentFromEvent(ev.PDU)
if err != nil {
return false, err
}
return plc.UserLevel(senderID) >= plc.NotificationLevel(levelKey), nil
}
return true, nil
verImpl := gomatrixserverlib.MustGetRoomVersion(createEvent.Version())
if verImpl.PrivilegedCreators() && slices.Contains(gomatrixserverlib.CreatorsFromCreateEvent(createEvent), string(senderID)) {
return true, nil
}
if plEvent == nil {
return true, nil // unsure, but this is what we did before
}
plc, err := gomatrixserverlib.NewPowerLevelContentFromEvent(plEvent.PDU)
if err != nil {
return false, err
}
return plc.UserLevel(senderID) >= plc.NotificationLevel(levelKey), nil
}
// localPushDevices pushes to the configured devices of a local

View file

@ -337,7 +337,7 @@ func (a *UserInternalAPI) PerformDeviceDeletion(ctx context.Context, req *api.Pe
deleteReq := &api.PerformDeleteKeysRequest{
UserID: req.UserID,
}
for _, keyID := range req.DeviceIDs {
for _, keyID := range deletedDeviceIDs {
deleteReq.KeyIDs = append(deleteReq.KeyIDs, gomatrixserverlib.KeyID(keyID))
}
deleteRes := &api.PerformDeleteKeysResponse{}