Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions cmd/gendocs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ func genFileDocs(filename string) {
return true
})
}

func isSourceFile(name string) bool {
return strings.HasSuffix(name, ".go") && !strings.HasSuffix(name, "_test.go")
}

func main() {
fmt.Println(`AUTOGENERATED FILE DO NOT EDIT

Expand All @@ -91,7 +96,7 @@ This file contains autogenerated "schema" for Babbleserv's FoundationDB keyvalue
fmt.Println("# Accounts Database")
fmt.Println("")
filepath.Walk("internal/databases/accounts", func(path string, info fs.FileInfo, err error) error {
if strings.HasSuffix(info.Name(), ".go") {
if isSourceFile(info.Name()) {
genFileDocs(path)
}
return nil
Expand All @@ -101,7 +106,7 @@ This file contains autogenerated "schema" for Babbleserv's FoundationDB keyvalue
fmt.Println("# Media Database")
fmt.Println("")
filepath.Walk("internal/databases/media", func(path string, info fs.FileInfo, err error) error {
if strings.HasSuffix(info.Name(), ".go") {
if isSourceFile(info.Name()) {
genFileDocs(path)
}
return nil
Expand All @@ -111,7 +116,7 @@ This file contains autogenerated "schema" for Babbleserv's FoundationDB keyvalue
fmt.Println("# Rooms Database")
fmt.Println("")
filepath.Walk("internal/databases/rooms", func(path string, info fs.FileInfo, err error) error {
if strings.HasSuffix(info.Name(), ".go") {
if isSourceFile(info.Name()) {
genFileDocs(path)
}
return nil
Expand All @@ -121,7 +126,7 @@ This file contains autogenerated "schema" for Babbleserv's FoundationDB keyvalue
fmt.Println("# System Database")
fmt.Println("")
filepath.Walk("internal/databases/system", func(path string, info fs.FileInfo, err error) error {
if strings.HasSuffix(info.Name(), ".go") {
if isSourceFile(info.Name()) {
genFileDocs(path)
}
return nil
Expand All @@ -131,7 +136,7 @@ This file contains autogenerated "schema" for Babbleserv's FoundationDB keyvalue
fmt.Println("# Transient Database")
fmt.Println("")
filepath.Walk("internal/databases/transient", func(path string, info fs.FileInfo, err error) error {
if strings.HasSuffix(info.Name(), ".go") {
if isSourceFile(info.Name()) {
genFileDocs(path)
}
return nil
Expand Down
7 changes: 7 additions & 0 deletions docker/complement-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ notifier:
rooms:
enabled: true
defaultVersion: 11
# Small, so that Complement stages over several transactions and runs an artifact job for every
# resolution with conflicts.
stateBudget:
stagingBytes: 8192
inlineResolutionCandidates: 1
# Every resolution with conflicts reads its auth difference in a job
inlineResolutionStateTuples: 1
accounts:
enabled: true
transient:
Expand Down
125 changes: 90 additions & 35 deletions docs/data-model-generated.md

Large diffs are not rendered by default.

282 changes: 282 additions & 0 deletions docs/event-sending.md

Large diffs are not rendered by default.

13 changes: 10 additions & 3 deletions docs/project-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ Babbleserv is designed to lean on FoundationDB as much as possible. Think of it

## Transactions

At the core of this lies transactions which FDB provides strong consistency guarantees for. By **evaluating event authorzation and state resolution within transactions** we simplify handling of both local and federated events significantly. See the `SendLocalEvents` and `SendFederatedEvents` in `internal/database/rooms/eventsend.go` which implement auth + store in single transactions.
At the core of this lies transactions which FDB provides strong consistency guarantees for. Large changes (state resets, big joins) must perform work across multiple transactions to avoid limits.

- [Room state storage](state-storage.md): FoundationDB records, immutable state trees, caching and membership indexes.
- [Event sending](event-sending.md): transaction flow, publication guards, staging, artifact jobs, retries and state resolution.

### Transaction Rules

Expand Down Expand Up @@ -45,6 +48,10 @@ Babbleserv is roughly divided in three:
- implement Matrix endpoints
- call through to database transactions

### `internal/federator/`
### `internal/workers/`

- asynchronous work after database changes, most as iterators over a database range, see [databases.md](databases.md#iterators-for-cross-database-transactions)

### `internal/stateres/`

- federator?
- state resolution v2 and v2.1 over the conflicting part of the states, see [event-sending.md](event-sending.md#state-resolution)
243 changes: 243 additions & 0 deletions docs/state-storage.md

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ require (
github.com/beeper/libserv v0.0.0-20231231202820-c7303abfc32c
github.com/go-chi/chi/v5 v5.0.12
github.com/jedib0t/go-pretty/v6 v6.7.5
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16
github.com/matrix-org/gomatrixserverlib v0.0.0-20240328203753-c2391f7113a5
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530
github.com/matrix-org/gomatrixserverlib v0.0.0-20260716140101-4fe595dc7f58
github.com/minio/minio-go/v7 v7.0.74
github.com/redis/go-redis/v9 v9.5.4
github.com/rs/xid v1.6.0
Expand All @@ -35,18 +35,20 @@ require (
github.com/goccy/go-json v0.10.3 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/go-set/v3 v3.0.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4 // indirect
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/oleiade/lane/v2 v2.0.0 // indirect
github.com/petermattis/goid v0.0.0-20251121121749-a11dd1a45f9a // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
Expand Down
13 changes: 13 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/jedib0t/go-pretty/v6 v6.7.5 h1:9dJSWTJnsXJVVAbvxIFxeHf/JxoJd7GUl5o3UzhtuiM=
github.com/jedib0t/go-pretty/v6 v6.7.5/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
Expand All @@ -48,10 +50,16 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 h1:ZtO5uywdd5dLDCud4r0r55eP4j9FuUNpl60Gmntcop4=
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
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-20240328203753-c2391f7113a5 h1:GuxmpyjZQoqb6UFQgKq8Td3wIITlXln/sItqp1jbTTA=
github.com/matrix-org/gomatrixserverlib v0.0.0-20240328203753-c2391f7113a5/go.mod h1:HZGsVJ3bUE+DkZtufkH9H0mlsvbhEGK5CpX0Zlavylg=
github.com/matrix-org/gomatrixserverlib v0.0.0-20260716140101-4fe595dc7f58 h1:S42otjye0YIfq58z0nnKmiOuZDXiy6XlbMpLUmTZAqE=
github.com/matrix-org/gomatrixserverlib v0.0.0-20260716140101-4fe595dc7f58/go.mod h1:b6KVfDjXjA5Q7vhpOaMqIhFYvu5BuFVZixlNeTV/CLc=
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4 h1:eCEHXWDv9Rm335MSuB49mFUK44bwZPFSDde3ORE3syk=
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4/go.mod h1:vVQlW/emklohkZnOPwD3LrZUBqdfsbiyO3p1lNV8F6U=
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 h1:6z4KxomXSIGWqhHcfzExgkH3Z3UkIXry4ibJS4Aqz2Y=
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66/go.mod h1:iBI1foelCqA09JJgPV0FYz4qA5dUXYOxMi57FxKBdd4=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
Expand All @@ -65,10 +73,13 @@ github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuE
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.74 h1:fTo/XlPBTSpo3BAMshlwKL5RspXRv9us5UeHEGYCFe0=
github.com/minio/minio-go/v7 v7.0.74/go.mod h1:qydcVzV8Hqtj1VtEocfxbmVFa2siu6HGa+LDEPogjD8=
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/petermattis/goid v0.0.0-20251121121749-a11dd1a45f9a h1:VweslR2akb/ARhXfqSfRbj1vpWwYXf3eeAUyw/ndms0=
github.com/petermattis/goid v0.0.0-20251121121749-a11dd1a45f9a/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
Expand All @@ -91,6 +102,8 @@ github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRo
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
Expand Down
45 changes: 45 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ type keyConfig struct {
ExpiredTimestamp int64 `yaml:"expiredTimestamp"`
}

// What a publish's transaction may hold before the room record, leaving room under FoundationDB's
// 10 MB for the record and a send's extra writes
const PublishMandatoryMaxBytes = 9_000_000

// Bounds on the work of one transaction when sending events to a room
type stateBudgetConfig struct {
// Approximate bytes staged per transaction, leaving headroom for FoundationDB overhead
// Default: 5 MB
StagingBytes int `yaml:"stagingBytes"`
// Candidate events of a state resolution run inline, past which an artifact job runs it
// Default: 2000
InlineResolutionCandidates int `yaml:"inlineResolutionCandidates"`
// State tuples of the largest input of a state resolution run inline, past which an artifact job
// runs it, as its auth difference reads the auth header of every state event of its inputs
// Default: 10000
InlineResolutionStateTuples int `yaml:"inlineResolutionStateTuples"`
}

type NotifierConfig struct {
RedisAddr string `yaml:"redisAddr"`
RedisChannel string `yaml:"redisChannel"`
Expand Down Expand Up @@ -53,6 +71,15 @@ type BabbleConfig struct {
// events sent per room in memory only.
// Default: 3h
CompactRoomNotificationsTimeout time.Duration `yaml:"compactRoomNotificationsTimeout"`

// Most initial_state events a create room request may carry, all published with the create
// event
// Default: 100
MaxCreateRoomInitialState int `yaml:"maxCreateRoomInitialState"`
// Default: 1000
MaxCreateRoomInvites int `yaml:"maxCreateRoomInvites"`

StateBudget stateBudgetConfig `yaml:"stateBudget"`
} `yaml:"rooms"`

Accounts struct {
Expand Down Expand Up @@ -176,12 +203,30 @@ func NewBabbleConfig(filename string, commitHash string) BabbleConfig {
cfg.Transient.PresenceTimeoutCheckInterval = time.Minute
}

if cfg.Rooms.DefaultVersion == "" {
cfg.Rooms.DefaultVersion = "11"
}
if cfg.Rooms.MaxNotificationsPerUserRoom == 0 {
cfg.Rooms.MaxNotificationsPerUserRoom = 100
}
if cfg.Rooms.CompactRoomNotificationsTimeout == 0 {
cfg.Rooms.CompactRoomNotificationsTimeout = 3 * time.Hour
}
if cfg.Rooms.MaxCreateRoomInitialState <= 0 {
cfg.Rooms.MaxCreateRoomInitialState = 100
}
if cfg.Rooms.MaxCreateRoomInvites <= 0 {
cfg.Rooms.MaxCreateRoomInvites = 1000
}
if cfg.Rooms.StateBudget.StagingBytes <= 0 {
cfg.Rooms.StateBudget.StagingBytes = 5_000_000
}
if cfg.Rooms.StateBudget.InlineResolutionCandidates <= 0 {
cfg.Rooms.StateBudget.InlineResolutionCandidates = 2000
}
if cfg.Rooms.StateBudget.InlineResolutionStateTuples <= 0 {
cfg.Rooms.StateBudget.InlineResolutionStateTuples = 10_000
}

return cfg
}
Expand Down
13 changes: 13 additions & 0 deletions internal/databases/accounts/remotedirectory.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ func (a *AccountsDatabase) EnsureRemoteDirectoryUsers(
return err
}

// EnsureUnindexedRemoteDirectoryUsers is EnsureRemoteDirectoryUsers for those of the users with
// neither a profile fetch queued nor a profile fetched, as when indexing every member of a room
func (a *AccountsDatabase) EnsureUnindexedRemoteDirectoryUsers(
ctx context.Context,
sources []types.RemoteUserDirectorySource,
now, lookupAt time.Time,
) error {
_, err := util.DoWriteTransaction(ctx, a.db, func(txn fdb.Transaction) (types.Nil, error) {
return nil, a.users.TxnEnsureUnindexedRemoteDirectoryUsers(txn, sources, now, lookupAt)
})
return err
}

func (a *AccountsDatabase) NextRemoteDirectoryProfileJob(
ctx context.Context,
now time.Time,
Expand Down
28 changes: 21 additions & 7 deletions internal/databases/accounts/users/remotedirectory.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,6 @@ func (u *UsersDirectory) TxnEnsureRemoteDirectoryUsers(
return err
}
current := u.txnGetRemoteProfileJob(txn, source.UserID)
if !source.Joined {
if current != nil {
txn.Clear(u.remoteProfileDueKey(*current))
txn.Clear(u.remoteProfileJobKey(source.UserID))
}
continue
}
user, err := u.txnGetUser(txn, source.UserID)
if err != nil {
return err
Expand Down Expand Up @@ -104,6 +97,27 @@ func (u *UsersDirectory) TxnEnsureRemoteDirectoryUsers(
return nil
}

// Call TxnEnsureRemoteDirectoryUsers for users with neither a profile fetch queued nor a profile stored
func (u *UsersDirectory) TxnEnsureUnindexedRemoteDirectoryUsers(
txn fdb.Transaction,
sources []types.RemoteUserDirectorySource,
now, lookupAt time.Time,
) error {
jobs := make([]fdb.FutureByteSlice, len(sources))
profiles := make([]fdb.FutureByteSlice, len(sources))
for i, source := range sources {
jobs[i] = txn.Get(u.remoteProfileJobKey(source.UserID))
profiles[i] = txn.Get(u.keyForProfile(source.UserID))
}
unindexed := make([]types.RemoteUserDirectorySource, 0, len(sources))
for i, source := range sources {
if jobs[i].MustGet() == nil && profiles[i].MustGet() == nil {
unindexed = append(unindexed, source)
}
}
return u.TxnEnsureRemoteDirectoryUsers(txn, unindexed, now, lookupAt)
}

func (u *UsersDirectory) TxnNextRemoteDirectoryProfileJob(
txn fdb.ReadTransaction,
now time.Time,
Expand Down
52 changes: 49 additions & 3 deletions internal/databases/eventsend.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ package databases

import (
"context"
"encoding/json"
"errors"
"fmt"
"slices"

"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"

"github.com/rs/zerolog"
Expand All @@ -28,13 +33,54 @@ func (d *Databases) SendFederatedEvents(
ctx context.Context,
roomID id.RoomID,
evs []*types.Event,
options rooms.SendFederatedEventsOptions,
given *rooms.GivenStates,
) (*rooms.SendEventsResult, error) {
return d.sendEventsFunc(ctx, roomID, func(userPushRules types.UserPushRulesMap, userRoomContext types.UserRoomContextMap) (*rooms.SendEventsResult, error) {
return d.Rooms.SendFederatedEvents(ctx, roomID, evs, userPushRules, userRoomContext, options)
return d.Rooms.SendFederatedEvents(ctx, roomID, evs, given, userPushRules, userRoomContext)
})
}

// Sends a local user's join to a room through another server, with the state before the join and
// its auth chain from the resident server's send_join response. While this server is not in the
// room the join takes the response state as the room's state, otherwise it is sent like any
// federated event, against that state and the room's current state.
func (d *Databases) SendRemoteJoin(
ctx context.Context,
roomID id.RoomID,
joinEv *types.Event,
stateEvs, authEvs []*types.Event,
) (*rooms.SendEventsResult, error) {
stateIDs := make([]id.EventID, len(stateEvs))
for i, ev := range stateEvs {
stateIDs[i] = ev.ID
}
res, err := d.SendFederatedEvents(ctx, roomID, []*types.Event{joinEv}, &rooms.GivenStates{
BeforeEvents: map[id.EventID][]id.EventID{joinEv.ID: stateIDs},
Events: slices.Concat(stateEvs, authEvs),
})
if err != nil {
return nil, err
}
dropped := len(res.Rejected) > 0 && errors.Is(res.Rejected[0].Error, rooms.ErrEventDropped)
softFailed := slices.ContainsFunc(res.Allowed, func(ev *types.Event) bool { return ev.SoftFailed }) ||
(len(res.Rejected) > 0 && res.Rejected[0].Event.SoftFailed)
if dropped || softFailed {
// Another join got this server into the room meanwhile. A dropped join is not stored and a
// soft failed one never becomes the user's membership, so the user joins with a new local
// event instead. Otherwise the send_join event is kept, as the other server has it and may
// have federated it already.
var content map[string]any
if err := json.Unmarshal(joinEv.Content, &content); err != nil {
return nil, fmt.Errorf("failed to parse join content: %w", err)
}
delete(content, "join_authorised_via_users_server")
return d.SendLocalEvents(ctx, roomID, []*types.PartialEvent{
types.NewPartialEvent(roomID, event.StateMember, joinEv.StateKey, joinEv.Sender, content),
}, rooms.SendLocalEventsOptions{})
}
return res, nil
}

func (d *Databases) sendEventsFunc(
ctx context.Context,
roomID id.RoomID,
Expand All @@ -53,7 +99,7 @@ func (d *Databases) sendEventsFunc(
}

// Get local users in room
memberships, err := d.Rooms.GetCurrentRoomLocalJoinedMemberships(ctx, roomID)
memberships, err := d.Rooms.LocalJoinedMembers(ctx, roomID)
if err != nil {
return nil, err
}
Expand Down
Loading
Loading