Skip to content

Conversation

@sampaiodiego
Copy link
Member

@sampaiodiego sampaiodiego commented Sep 30, 2025

Proposed changes (including videos or screenshots)

Issue(s)

FDR-190

Steps to test or reproduce

Further comments

Summary by CodeRabbit

  • New Features

    • Federation HTTP endpoints are now served directly by the main application.
    • New, reusable federation setup and route-builder utilities are exposed for initialization and routing.
  • Refactor

    • Initialization is lifecycle-driven and settings-aware for simpler, more reliable startup and route registration.
    • Settings retrieval API made generically typed for safer consumption.
    • Startup and route wiring streamlined with improved error logging.
  • Chores

    • Removed the legacy standalone federation service package, config and changelog entries.

@sampaiodiego sampaiodiego requested a review from a team as a code owner September 30, 2025 23:59
Copilot AI review requested due to automatic review settings September 30, 2025 23:59
@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Sep 30, 2025

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link

changeset-bot bot commented Sep 30, 2025

⚠️ No Changeset found

Latest commit: 572d841

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 30, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Replaces the standalone federation-service with an in-repo federation-matrix flow: adds setupFederationMatrix and getFederationRoutes, refactors FederationMatrix to lifecycle-based initialization, removes IFederationMatrixService.getAllRoutes and ee/apps/federation-service package, and makes Settings.getValueById generic.

Changes

Cohort / File(s) Summary
Meteor EE API routing
apps/meteor/ee/server/api/federation.ts
Switches from service-provided routes to getFederationRoutes(); removes federationService/lifecycle guards; registers routes unconditionally in try/catch; changes logging shape and makes registerFederationRoutes() a no-arg async function.
Meteor EE startup flow
apps/meteor/ee/server/startup/federation.ts
Replaces managed federation-service lifecycle with setupFederationMatrix(instanceId) and direct api.registerService(new FederationMatrix()); removes legacy domain/license/watch-based start/stop logic and consolidates event/route wiring.
Remove legacy federation-service package
ee/apps/federation-service/*
ee/apps/federation-service/package.json, ee/apps/federation-service/src/service.ts, ee/apps/federation-service/src/config.ts, ee/apps/federation-service/.eslintrc.json, ee/apps/federation-service/tsconfig.json, ee/apps/federation-service/CHANGELOG.md
Deletes package manifest, startup/server module, config, tsconfig, ESLint config, and changelog entries — removing the standalone federation-service and its startup/health logic.
Meteor app deps
apps/meteor/package.json
Removes workspace dependency @rocket.chat/federation-service from Meteor app dependencies.
FederationMatrix lifecycle & public surface
ee/packages/federation-matrix/src/FederationMatrix.ts, ee/packages/federation-matrix/src/index.ts, ee/packages/federation-matrix/package.json, ee/packages/federation-matrix/tsconfig.json
Converts FederationMatrix to lifecycle created() init (removes static factory and route getters), updates package entry points to dist/index.*, and adds index re-exports (FederationMatrix, generateEd25519RandomSecretKey, getFederationRoutes, setupFederationMatrix).
Federation HTTP routes
ee/packages/federation-matrix/src/api/routes.ts
Adds getFederationRoutes() returning { matrix, wellKnown } routers, composes middleware (auth/license/domain) and mounts matrix subroutes (keys, versions, invites, profiles, rooms, send-join, transactions, media).
Federation setup initializer
ee/packages/federation-matrix/src/setup.ts
Adds setupFederationMatrix(instanceId) that validates domain/license, constructs config/container/emitter, registers event handlers conditioned on settings (typing/presence), and wires the federation container.
Core services interface
packages/core-services/src/types/IFederationMatrixService.ts
Removes getAllRoutes() from the IFederationMatrixService interface (routing no longer exposed via core service).
Settings typing changes
packages/model-typings/src/models/ISettingsModel.ts, packages/models/src/models/Settings.ts
Makes getValueById generic: `getValueById(_id: string): Promise<T

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Meteor as Meteor EE Startup
  participant Settings as Settings
  participant License as License
  participant Setup as setupFederationMatrix
  participant Fed as FederationMatrix
  participant WebApp as WebApp.rawConnectHandlers

  Meteor->>Setup: setupFederationMatrix(instanceId)
  Setup->>Settings: read federation settings
  Setup->>License: check License.hasModule('federation')
  alt enabled & valid domain
    Setup->>Setup: create Config / Emitter / Container
    Setup->>Fed: register events (typing/presence)
    Meteor->>Fed: api.registerService(new FederationMatrix())
    Meteor->>WebApp: getFederationRoutes()
    WebApp->>WebApp: register /_matrix and /.well-known handlers
  else disabled/invalid
    Setup-->>Meteor: log and exit early
  end
Loading
sequenceDiagram
  autonumber
  participant Client as Remote HS
  participant WebApp as WebApp Handlers
  participant Routes as getFederationRoutes()
  participant Subroutes as Matrix Subroutes
  participant FedService as FederationMatrix

  Client->>WebApp: HTTP request (/_matrix/... or /.well-known/...)
  WebApp->>Routes: dispatch
  Routes->>Subroutes: apply middleware (auth/license/domain)
  Subroutes->>FedService: invoke handler (keys/rooms/media/tx...)
  FedService-->>Subroutes: response
  Subroutes-->>WebApp: send response
  WebApp-->>Client: result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

stat: ready to merge, stat: QA assured

Suggested reviewers

  • ggazzo
  • rodrigok

Poem

I nibbled wires and hopped through code,
Routes found burrows on the new road.
Settings hummed and matrix stirred,
Events replied when callbacks heard.
A carrot cheer — federation soared! 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The pull request introduces changes to model-typings and settings models—specifically making getValueById generic in ISettingsModel and Settings—that do not directly relate to enabling federation in a microservices deployment and could be considered unrelated to the FDR-190 objective. Consider isolating the generic getValueById signature changes into a separate pull request or providing justification for how they are necessary to support the microservices migration to keep the scope focused on FDR-190.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly identifies the primary change by indicating a regression fix for native federation functionality with microservices, clearly aligning with the main purpose of the pull request without extraneous detail.
Linked Issues Check ✅ Passed All code modifications collectively migrate federation functionality from a standalone service towards a native microservices architecture by integrating setupFederationMatrix, getFederationRoutes, and FederationMatrix lifecycle management, removing the legacy standalone federation-service and updating Meteor startup logic to register the new federation microservice, thereby directly addressing the linked FDR-190 requirement.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch native-federation-ms

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the native federation service to resolve compatibility issues with microservices architecture by simplifying the service initialization and removing the standalone federation service app.

  • Refactored federation matrix service initialization to use a setup function instead of complex create method
  • Removed dedicated federation-service microservice app and its configuration files
  • Simplified federation routes registration and moved HTTP route management to separate modules

Reviewed Changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/models/src/models/Settings.ts Added generic type support to getValueById method for type-safe setting retrieval
packages/model-typings/src/models/ISettingsModel.ts Updated interface to support generic typing for getValueById method
packages/core-services/src/types/IFederationMatrixService.ts Removed getAllRoutes method from service interface
ee/packages/federation-matrix/tsconfig.json Changed entry point from FederationMatrix.ts to index.ts
ee/packages/federation-matrix/src/setup.ts New setup function for initializing federation with configuration validation
ee/packages/federation-matrix/src/index.ts New entry point exporting all federation matrix functionality
ee/packages/federation-matrix/src/api/routes.ts New module for managing federation HTTP routes
ee/packages/federation-matrix/src/FederationMatrix.ts Refactored service class to remove initialization complexity and route management
ee/packages/federation-matrix/package.json Updated main entry point to use new index.js
ee/apps/federation-service/* Removed entire federation-service microservice app
apps/meteor/package.json Removed federation-service dependency
apps/meteor/ee/server/startup/federation.ts Simplified federation startup using new setup function
apps/meteor/ee/server/api/federation.ts Updated to use new route registration approach

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

} else if (_id === 'Federation_Service_EDU_Process_Typing' && typeof value === 'boolean') {
this.processEDUTyping = value;
} else if (_id === 'Federation_Service_EDU_Process_Presence' && typeof value === 'boolean') {
this.processEDUTyping = value;
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be setting this.processEDUPresence = value instead of this.processEDUTyping = value for the 'Federation_Service_EDU_Process_Presence' setting.

Suggested change
this.processEDUTyping = value;
this.processEDUPresence = value;

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/meteor/ee/server/api/federation.ts (1)

16-23: Require matrixIds in the schema to avoid runtime crashes
FederationMatrix.verifyMatrixIds() (ee/packages/federation-matrix/src/FederationMatrix.ts) assumes it receives an array and immediately maps over it. If a caller omits matrixIds, the current schema lets the request through and we end up invoking .map on undefined, yielding a 500 instead of a clean validation error. Please mark matrixIds as required so Ajv blocks malformed requests up front.

Apply this diff to make the field mandatory:

 		query: ajv.compile<{
 			matrixIds: string[];
 		}>({
 			type: 'object',
 			properties: {
 				matrixIds: { type: 'array', items: { type: 'string' } },
 			},
+			required: ['matrixIds'],
 		}),
🧹 Nitpick comments (2)
ee/packages/federation-matrix/src/FederationMatrix.ts (1)

98-100: Settings retrieval pattern has code duplication with setup.ts.

Lines 98-100 retrieve the same settings (Federation_Service_Domain, Federation_Service_EDU_Process_Typing, Federation_Service_EDU_Process_Presence) as lines 37, 45-46 in setup.ts. This duplication could lead to inconsistencies if the settings IDs or default values change.

Consider extracting a shared function to retrieve federation settings:

// In a shared module:
export async function getFederationSettings() {
  return {
    serverName: (await Settings.getValueById<string>('Federation_Service_Domain')) || '',
    processEDUTyping: (await Settings.getValueById<boolean>('Federation_Service_EDU_Process_Typing')) || false,
    processEDUPresence: (await Settings.getValueById<boolean>('Federation_Service_EDU_Process_Presence')) || false,
  };
}

Then use it in both files:

const { serverName, processEDUTyping, processEDUPresence } = await getFederationSettings();
ee/packages/federation-matrix/src/setup.ts (1)

36-37: Enforce runtime type validation in Settings.getValueById<T>
The current implementation in packages/models/src/models/Settings.ts simply does:

return setting?.value as T;

without any runtime checks against T. If the stored value’s type doesn’t match the requested generic, you’ll get a silent cast error at runtime. Introduce a type‐guard or explicit validation (e.g. typeof checks or a schema validator) to ensure setting.value truly conforms to T before returning.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3f22549 and aab9ee5.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (18)
  • apps/meteor/ee/server/api/federation.ts (2 hunks)
  • apps/meteor/ee/server/startup/federation.ts (1 hunks)
  • apps/meteor/package.json (0 hunks)
  • ee/apps/federation-service/.eslintrc.json (0 hunks)
  • ee/apps/federation-service/CHANGELOG.md (0 hunks)
  • ee/apps/federation-service/package.json (0 hunks)
  • ee/apps/federation-service/src/config.ts (0 hunks)
  • ee/apps/federation-service/src/service.ts (0 hunks)
  • ee/apps/federation-service/tsconfig.json (0 hunks)
  • ee/packages/federation-matrix/package.json (1 hunks)
  • ee/packages/federation-matrix/src/FederationMatrix.ts (5 hunks)
  • ee/packages/federation-matrix/src/api/routes.ts (1 hunks)
  • ee/packages/federation-matrix/src/index.ts (1 hunks)
  • ee/packages/federation-matrix/src/setup.ts (1 hunks)
  • ee/packages/federation-matrix/tsconfig.json (1 hunks)
  • packages/core-services/src/types/IFederationMatrixService.ts (0 hunks)
  • packages/model-typings/src/models/ISettingsModel.ts (2 hunks)
  • packages/models/src/models/Settings.ts (2 hunks)
💤 Files with no reviewable changes (8)
  • packages/core-services/src/types/IFederationMatrixService.ts
  • ee/apps/federation-service/.eslintrc.json
  • apps/meteor/package.json
  • ee/apps/federation-service/package.json
  • ee/apps/federation-service/tsconfig.json
  • ee/apps/federation-service/src/service.ts
  • ee/apps/federation-service/src/config.ts
  • ee/apps/federation-service/CHANGELOG.md
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: rodrigok
PR: RocketChat/Rocket.Chat#36991
File: apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts:219-221
Timestamp: 2025-09-19T15:15:04.642Z
Learning: The Federation_Matrix_homeserver_domain setting in apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts is part of the old federation system and is being deprecated/removed, so configuration issues with this setting should not be flagged for improvement.
📚 Learning: 2025-09-19T15:15:04.642Z
Learnt from: rodrigok
PR: RocketChat/Rocket.Chat#36991
File: apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts:219-221
Timestamp: 2025-09-19T15:15:04.642Z
Learning: The Federation_Matrix_homeserver_domain setting in apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts is part of the old federation system and is being deprecated/removed, so configuration issues with this setting should not be flagged for improvement.

Applied to files:

  • ee/packages/federation-matrix/src/setup.ts
  • apps/meteor/ee/server/startup/federation.ts
  • apps/meteor/ee/server/api/federation.ts
  • ee/packages/federation-matrix/src/FederationMatrix.ts
🧬 Code graph analysis (5)
ee/packages/federation-matrix/src/setup.ts (3)
ee/packages/federation-matrix/src/index.ts (1)
  • setupFederationMatrix (9-9)
packages/core-services/src/index.ts (1)
  • License (162-162)
ee/packages/federation-matrix/src/events/index.ts (1)
  • registerEvents (11-22)
ee/packages/federation-matrix/src/api/routes.ts (12)
ee/packages/federation-matrix/src/api/middlewares/isFederationEnabled.ts (1)
  • isFederationEnabledMiddleware (4-9)
ee/packages/federation-matrix/src/api/middlewares/isLicenseEnabled.ts (1)
  • isLicenseEnabledMiddleware (4-9)
ee/packages/federation-matrix/src/api/_matrix/key/server.ts (1)
  • getKeyServerRoutes (35-56)
ee/packages/federation-matrix/src/api/_matrix/versions.ts (1)
  • getFederationVersionsRoutes (28-54)
ee/packages/federation-matrix/src/api/middlewares/isFederationDomainAllowed.ts (1)
  • isFederationDomainAllowedMiddleware (36-61)
ee/packages/federation-matrix/src/api/_matrix/invite.ts (1)
  • getMatrixInviteRoutes (295-351)
ee/packages/federation-matrix/src/api/_matrix/profiles.ts (1)
  • getMatrixProfilesRoutes (352-499)
ee/packages/federation-matrix/src/api/_matrix/rooms.ts (1)
  • getMatrixRoomsRoutes (124-207)
ee/packages/federation-matrix/src/api/_matrix/send-join.ts (1)
  • getMatrixSendJoinRoutes (224-250)
ee/packages/federation-matrix/src/api/_matrix/transactions.ts (1)
  • getMatrixTransactionsRoutes (316-506)
ee/packages/federation-matrix/src/api/_matrix/media.ts (1)
  • getMatrixMediaRoutes (76-153)
ee/packages/federation-matrix/src/api/.well-known/server.ts (1)
  • getWellKnownRoutes (22-46)
apps/meteor/ee/server/startup/federation.ts (3)
ee/packages/federation-matrix/src/setup.ts (1)
  • setupFederationMatrix (35-103)
ee/packages/federation-matrix/src/FederationMatrix.ts (1)
  • FederationMatrix (28-887)
apps/meteor/ee/server/api/federation.ts (1)
  • registerFederationRoutes (43-52)
apps/meteor/ee/server/api/federation.ts (2)
ee/packages/federation-matrix/src/FederationMatrix.ts (1)
  • FederationMatrix (28-887)
ee/packages/federation-matrix/src/api/routes.ts (1)
  • getFederationRoutes (17-39)
ee/packages/federation-matrix/src/FederationMatrix.ts (2)
packages/core-typings/src/IUser.ts (1)
  • IUser (186-255)
ee/packages/federation-matrix/src/services/MatrixMediaService.ts (1)
  • MatrixMediaService (18-151)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (5)
packages/model-typings/src/models/ISettingsModel.ts (1)

1-16: Generic signature reads well.

The generic return type lets callers opt into stronger typing without breaking default behavior. 👍

packages/models/src/models/Settings.ts (1)

24-28: Implementation matches the new typing.

Returning setting?.value as T keeps runtime the same while exposing the new compile-time flexibility.

ee/packages/federation-matrix/src/setup.ts (1)

8-33: Domain validation logic looks solid.

The validation correctly checks for empty strings, uppercase letters, and URL validity. Good use of console.error for logging validation failures.

ee/packages/federation-matrix/src/FederationMatrix.ts (2)

816-839: Approve typing notification implementation.

The notifyUserTyping method correctly checks the processEDUTyping flag and validates the room and user before sending typing notifications. The use of void on line 838 is appropriate for fire-and-forget behavior.


61-63: Remove unnecessary multi-file search for processEDUPresence
The processEDUPresence flag is only referenced in FederationMatrix.ts, so running rg across the repo is redundant. After fixing the assignment on line 52, ensure your unit tests cover EDU-presence gating.

Likely an incorrect or invalid review comment.

Comment on lines 39 to 55
async created(): Promise<void> {
// although this is async function, it is not awaited, so we need to register the listeners before everything else
this.onEvent('watch.settings', async ({ clientAction, setting }): Promise<void> => {
if (clientAction === 'removed') {
return;
}

static async create(instanceId: string, emitter?: Emitter<HomeserverEventSignatures>): Promise<FederationMatrix> {
const instance = new FederationMatrix(emitter);
const settingsSigningAlg = await Settings.get<string>('Federation_Service_Matrix_Signing_Algorithm');
const settingsSigningVersion = await Settings.get<string>('Federation_Service_Matrix_Signing_Version');
const settingsSigningKey = await Settings.get<string>('Federation_Service_Matrix_Signing_Key');
const serverHostname = (await Settings.get<string>('Federation_Service_Domain')).trim();

instance.serverName = serverHostname;

instance.processEDUTyping = await Settings.get<boolean>('Federation_Service_EDU_Process_Typing');
instance.processEDUPresence = await Settings.get<boolean>('Federation_Service_EDU_Process_Presence');

const mongoUri = process.env.MONGO_URL || 'mongodb://localhost:3001/meteor';

const dbName = process.env.DATABASE_NAME || new URL(mongoUri).pathname.slice(1);

const config = new ConfigService({
instanceId,
serverName: serverHostname,
keyRefreshInterval: Number.parseInt(process.env.MATRIX_KEY_REFRESH_INTERVAL || '60', 10),
matrixDomain: serverHostname,
version: process.env.SERVER_VERSION || '1.0',
port: Number.parseInt(process.env.SERVER_PORT || '8080', 10),
signingKey: `${settingsSigningAlg} ${settingsSigningVersion} ${settingsSigningKey}`,
signingKeyPath: process.env.CONFIG_FOLDER || './rocketchat.signing.key',
database: {
uri: mongoUri,
name: dbName,
poolSize: Number.parseInt(process.env.DATABASE_POOL_SIZE || '10', 10),
},
media: {
maxFileSize: Number.parseInt(process.env.MEDIA_MAX_FILE_SIZE || '100', 10) * 1024 * 1024,
allowedMimeTypes: process.env.MEDIA_ALLOWED_MIME_TYPES?.split(',') || [
'image/jpeg',
'image/png',
'image/gif',
'image/webp',
'text/plain',
'application/pdf',
'video/mp4',
'audio/mpeg',
'audio/ogg',
],
enableThumbnails: process.env.MEDIA_ENABLE_THUMBNAILS === 'true' || true,
rateLimits: {
uploadPerMinute: Number.parseInt(process.env.MEDIA_UPLOAD_RATE_LIMIT || '10', 10),
downloadPerMinute: Number.parseInt(process.env.MEDIA_DOWNLOAD_RATE_LIMIT || '60', 10),
},
},
const { _id, value } = setting;
if (_id === 'Federation_Service_Domain' && typeof value === 'string') {
this.serverName = value;
} else if (_id === 'Federation_Service_EDU_Process_Typing' && typeof value === 'boolean') {
this.processEDUTyping = value;
} else if (_id === 'Federation_Service_EDU_Process_Presence' && typeof value === 'boolean') {
this.processEDUTyping = value;
} else {
return;
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid async operations in lifecycle hooks without proper coordination.

The created() method registers an async event handler for watch.settings (line 41), but the comment on line 40 notes that the function is not awaited. This means the handler registration completes synchronously, but if settings updates arrive before the initialization at lines 98-108 completes, this.homeserverServices may be undefined, causing runtime errors in other methods.

Consider one of these approaches:

  1. Initialize critical fields (serverName, processEDUTyping, processEDUPresence) before registering the watcher.
  2. Add guards in methods that depend on this.homeserverServices to handle undefined state gracefully.
  3. Make the watcher registration await initialization completion.

Current code already initializes these fields at lines 98-100, so the issue is mitigated, but the comment suggests this is fragile.

🤖 Prompt for AI Agents
In ee/packages/federation-matrix/src/FederationMatrix.ts around lines 39-56, the
created() lifecycle registers an async 'watch.settings' handler before critical
fields (serverName, processEDUTyping, processEDUPresence / homeserverServices)
are guaranteed initialized, risking undefined access; fix by initializing those
critical fields (read current settings or set safe defaults) before you register
the watcher, or alternatively gate the handler with a simple initializationReady
flag/promise and await it inside the handler (or delay registration until after
lines 98-108 finish) so any incoming setting events cannot observe an
uninitialized homeserverServices or related state.

Comment on lines 102 to 107
try {
registerEvents(this.eventHandler, this.serverName, { typing: this.processEDUTyping, presence: this.processEDUPresence });
} catch (error) {
this.logger.warn('Homeserver module not available, running in limited mode');
}
}

getAllRoutes() {
return this.httpRoutes;
}
this.homeserverServices = getAllServices();

getServerName(): string {
return this.serverName;
MatrixMediaService.setHomeserverServices(this.homeserverServices);
} catch (err) {
this.logger.warn({ msg: 'Homeserver module not available, running in limited mode', err });
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Improve error handling and service availability checks.

The try-catch block (lines 102-108) catches errors during getAllServices() and logs a warning, but doesn't set a flag to indicate limited mode. Later methods check if (!this.homeserverServices) but this check will always be false after the catch block, leading to potential null pointer exceptions.

Apply this pattern to properly handle missing services:

 		try {
 			this.homeserverServices = getAllServices();
 
 			MatrixMediaService.setHomeserverServices(this.homeserverServices);
 		} catch (err) {
 			this.logger.warn({ msg: 'Homeserver module not available, running in limited mode', err });
+			// Explicitly set to undefined so checks throughout the class work correctly
+			this.homeserverServices = undefined as any;
 		}

Better yet, check the result after the call:

 		try {
-			this.homeserverServices = getAllServices();
-
-			MatrixMediaService.setHomeserverServices(this.homeserverServices);
+			const services = getAllServices();
+			if (services) {
+				this.homeserverServices = services;
+				MatrixMediaService.setHomeserverServices(this.homeserverServices);
+			} else {
+				this.logger.warn('Homeserver module not available, running in limited mode');
+			}
 		} catch (err) {
 			this.logger.warn({ msg: 'Homeserver module not available, running in limited mode', err });
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
registerEvents(this.eventHandler, this.serverName, { typing: this.processEDUTyping, presence: this.processEDUPresence });
} catch (error) {
this.logger.warn('Homeserver module not available, running in limited mode');
}
}
getAllRoutes() {
return this.httpRoutes;
}
this.homeserverServices = getAllServices();
getServerName(): string {
return this.serverName;
MatrixMediaService.setHomeserverServices(this.homeserverServices);
} catch (err) {
this.logger.warn({ msg: 'Homeserver module not available, running in limited mode', err });
}
try {
- this.homeserverServices = getAllServices();
-
const services = getAllServices();
if (services) {
this.homeserverServices = services;
MatrixMediaService.setHomeserverServices(this.homeserverServices);
} else {
this.logger.warn('Homeserver module not available, running in limited mode');
}
} catch (err) {
this.logger.warn({ msg: 'Homeserver module not available, running in limited mode', err });
}

Comment on lines +52 to +54
const mongoUri = process.env.MONGO_URL || 'mongodb://localhost:3001/meteor';
const dbName = process.env.DATABASE_NAME || new URL(mongoUri).pathname.slice(1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify MongoDB URI parsing fallback.

The code parses DATABASE_NAME from MONGO_URL using new URL(mongoUri).pathname.slice(1), but if MONGO_URL is malformed or the pathname is empty, this could fail or produce an incorrect database name.


🏁 Script executed:

#!/bin/bash
# Check if there are any validation utilities or tests for MONGO_URL parsing
rg -nP --type=ts -C3 'MONGO_URL.*pathname|DATABASE_NAME' -g '!node_modules' -g '!dist'

Length of output: 148


#!/bin/bash
set -eux
# Locate the setup.ts file
fd "setup.ts" ee/packages/federation-matrix/src
# Print lines 1–100 to inspect the parsing code
sed -n '1,100p' ee/packages/federation-matrix/src/setup.ts
# Search within that file for MONGO_URL usage
rg -n -C3 "MONGO_URL" ee/packages/federation-matrix/src/setup.ts

#!/bin/bash
set -eux
# Locate and display setup.ts
fd --full-path "ee/packages/federation-matrix/src/setup.ts"
sed -n '1,80p' ee/packages/federation-matrix/src/setup.ts
# Search specifically for MONGO_URL and DATABASE_NAME usage
rg -n -C2 "MONGO_URL|DATABASE_NAME" ee/packages/federation-matrix/src/setup.ts

🏁 Script executed:

#!/bin/bash
set -eux

# Locate setup.ts
fd --full-path "setup.ts" ee/packages/federation-matrix/src

# Print lines around the parsing logic
sed -n '40,80p' ee/packages/federation-matrix/src/setup.ts

# Search that range for fallback or validation logic
rg -n -C2 "new URL" ee/packages/federation-matrix/src/setup.ts
rg -n -C2 "slice" ee/packages/federation-matrix/src/setup.ts
rg -n -C2 "DATABASE_NAME" ee/packages/federation-matrix/src/setup.ts

Length of output: 2910


Add validation and fallback for MongoDB URI parsing
Wrap the new URL(mongoUri).pathname.slice(1) call in a try/catch (at ee/packages/federation-matrix/src/setup.ts lines 52–53) and provide a sensible default or explicit error if the URI is malformed or its pathname is empty.

🤖 Prompt for AI Agents
In ee/packages/federation-matrix/src/setup.ts around lines 52 to 53, wrap the
new URL(mongoUri).pathname.slice(1) call in a try/catch and validate the parsed
pathname; if URL parsing fails or the pathname is empty, fall back to a sensible
default (e.g., 'meteor') or use an explicit error path (throw or
processLogger.error and process.exit(1)) so the app doesn't continue with an
invalid DB name; ensure you prefer process.env.DATABASE_NAME when present and
only use the parsed value when valid.

@codecov
Copy link

codecov bot commented Oct 1, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 67.39%. Comparing base (ec81ab4) to head (572d841).
⚠️ Report is 3 commits behind head on release-7.11.0.

Additional details and impacted files

Impacted file tree graph

@@               Coverage Diff                @@
##           release-7.11.0   #37110    +/-   ##
================================================
  Coverage           67.39%   67.39%            
================================================
  Files                3328     3330     +2     
  Lines              113353   113493   +140     
  Branches            20565    20601    +36     
================================================
+ Hits                76389    76488    +99     
- Misses              34359    34385    +26     
- Partials             2605     2620    +15     
Flag Coverage Δ
e2e 57.29% <ø> (+0.03%) ⬆️
e2e-api 40.90% <ø> (+0.46%) ⬆️
unit 71.12% <ø> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (5)
ee/packages/federation-matrix/src/setup.ts (2)

52-54: Add validation and fallback for MongoDB URI parsing.

The MongoDB URI parsing on line 54 using new URL(mongoUri).pathname.slice(1) can fail if:

  1. The mongoUri is malformed
  2. The pathname is empty (would result in empty string as database name)

Wrap the parsing in a try-catch and validate the result:

 	// TODO are these required?
 	const mongoUri = process.env.MONGO_URL || 'mongodb://localhost:3001/meteor';
-	const dbName = process.env.DATABASE_NAME || new URL(mongoUri).pathname.slice(1);
+	let dbName = process.env.DATABASE_NAME;
+	if (!dbName) {
+		try {
+			const parsed = new URL(mongoUri).pathname.slice(1);
+			dbName = parsed || 'meteor';
+		} catch (error) {
+			console.error('Failed to parse MONGO_URL, using default database name', error);
+			dbName = 'meteor';
+		}
+	}

83-83: Fix redundant boolean comparison.

Line 83 uses process.env.MEDIA_ENABLE_THUMBNAILS === 'true' || true, which always evaluates to true regardless of the environment variable value. This makes the conditional check ineffective.

Apply this diff:

-			enableThumbnails: process.env.MEDIA_ENABLE_THUMBNAILS === 'true' || true,
+			enableThumbnails: process.env.MEDIA_ENABLE_THUMBNAILS !== 'false',

Or if you want strict opt-in behavior:

-			enableThumbnails: process.env.MEDIA_ENABLE_THUMBNAILS === 'true' || true,
+			enableThumbnails: process.env.MEDIA_ENABLE_THUMBNAILS === 'true',
ee/packages/federation-matrix/src/FederationMatrix.ts (3)

52-53: Fix copy-paste error in settings watcher.

Line 52 incorrectly assigns the Federation_Service_EDU_Process_Presence setting value to this.processEDUTyping instead of this.processEDUPresence. This is a critical logic bug that will cause presence events to be processed incorrectly.

Apply this diff:

 		} else if (_id === 'Federation_Service_EDU_Process_Presence' && typeof value === 'boolean') {
-			this.processEDUTyping = value;
+			this.processEDUPresence = value;
 		}

40-55: Race condition: settings watcher may fire before initialization completes.

The created() method registers the settings watcher immediately (line 42), but homeserverServices isn't initialized until lines 101-107. If a settings change event arrives during this window and triggers the handler, methods accessing this.homeserverServices (like the presence handler at line 85) may encounter undefined state.

Consider initializing homeserverServices before registering the watcher, or add a guard in the presence handler:

 	async created(): Promise<void> {
+		this.serverName = (await Settings.getValueById<string>('Federation_Service_Domain')) || '';
+		this.processEDUTyping = (await Settings.getValueById<boolean>('Federation_Service_EDU_Process_Typing')) || false;
+		this.processEDUPresence = (await Settings.getValueById<boolean>('Federation_Service_EDU_Process_Presence')) || false;
+
+		try {
+			this.homeserverServices = getAllServices();
+			MatrixMediaService.setHomeserverServices(this.homeserverServices);
+		} catch (err) {
+			this.logger.warn({ msg: 'Homeserver module not available, running in limited mode', err });
+		}
+
 		// although this is async function, it is not awaited, so we need to register the listeners before everything else
 		this.onEvent('watch.settings', async ({ clientAction, setting }): Promise<void> => {

101-108: Improve error handling for service initialization.

If getAllServices() throws an exception before assignment (line 102), this.homeserverServices remains uninitialized (not explicitly undefined). Methods throughout the class check if (!this.homeserverServices), but this guard may not work correctly with an uninitialized field.

Explicitly handle the undefined state:

+		this.homeserverServices = undefined;
+
 		try {
 			this.homeserverServices = getAllServices();
 
 			MatrixMediaService.setHomeserverServices(this.homeserverServices);
 		} catch (err) {
 			this.logger.warn({ msg: 'Homeserver module not available, running in limited mode', err });
 		}

This ensures the field has a defined value even when initialization fails.

🧹 Nitpick comments (4)
ee/packages/federation-matrix/src/setup.ts (1)

9-34: Consider using structured logging.

The validateDomain function uses console.error for error messages. Since this file imports and uses other infrastructure like Settings and License, consider using a proper logger (e.g., Logger from @rocket.chat/logger) for consistency with the rest of the codebase and better observability.

Import and use Logger:

+import { Logger } from '@rocket.chat/logger';
+
+const processLogger = new Logger('federation-matrix:setup');
+
 function validateDomain(domain: string): boolean {
 	const value = domain.trim();
 
 	if (!value) {
-		console.error('The Federation domain is not set');
+		processLogger.error('The Federation domain is not set');
 		return false;
 	}
 
 	if (value.toLowerCase() !== value) {
-		console.error(`The Federation domain "${value}" cannot have uppercase letters`);
+		processLogger.error(`The Federation domain "${value}" cannot have uppercase letters`);
 		return false;
 	}
 
 	try {
 		const valid = new URL(`https://${value}`).hostname === value;
 
 		if (!valid) {
 			throw new Error();
 		}
 	} catch {
-		console.error(`The configured Federation domain "${value}" is not valid`);
+		processLogger.error(`The configured Federation domain "${value}" is not valid`);
 		return false;
 	}
 
 	return true;
 }

Similarly update line 42:

-	console.log('Federation service is disabled');
+	processLogger.info('Federation service is disabled');
ee/packages/federation-matrix/src/FederationMatrix.ts (3)

36-36: Consider making homeserverServices nullable in type declaration.

The homeserverServices field is declared without initialization and without a nullable type. Methods throughout the class check if (!this.homeserverServices), but TypeScript won't enforce this guard if the field is typed as non-nullable.

Make the type explicitly nullable:

-	private homeserverServices: HomeserverServices;
+	private homeserverServices: HomeserverServices | undefined;

This provides better type safety and makes it clear that the field may be uninitialized.


85-93: Consider error handling for fire-and-forget async call.

Line 85 uses void to explicitly ignore the promise returned by sendPresenceUpdateToRooms. While this fire-and-forget pattern may be intentional for performance, any errors in the presence update will be silently lost.

Consider adding error handling:

-			void this.homeserverServices.edu.sendPresenceUpdateToRooms(
+			this.homeserverServices.edu.sendPresenceUpdateToRooms(
 				[
 					{
 						user_id: localUser.federation.mui,
 						presence: statusMap[user.status] || 'offline',
 					},
 				],
 				roomsUserIsMemberOf.map(({ externalRoomId }) => externalRoomId).filter(Boolean),
-			);
+			).catch((error) => {
+				this.logger.error('Failed to send presence update', error);
+			});

837-837: Consider error handling for fire-and-forget async call.

Line 837 uses void to ignore the promise from sendTypingNotification. Similar to the presence update at line 85, errors will be silently lost.

Add error handling:

-		void this.homeserverServices.edu.sendTypingNotification(room.federation.mrid, userMui, isTyping);
+		this.homeserverServices.edu.sendTypingNotification(room.federation.mrid, userMui, isTyping)
+			.catch((error) => {
+				this.logger.error('Failed to send typing notification', error);
+			});
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between aab9ee5 and 345e5c4.

📒 Files selected for processing (2)
  • ee/packages/federation-matrix/src/FederationMatrix.ts (5 hunks)
  • ee/packages/federation-matrix/src/setup.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-19T15:15:04.642Z
Learnt from: rodrigok
PR: RocketChat/Rocket.Chat#36991
File: apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts:219-221
Timestamp: 2025-09-19T15:15:04.642Z
Learning: The Federation_Matrix_homeserver_domain setting in apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts is part of the old federation system and is being deprecated/removed, so configuration issues with this setting should not be flagged for improvement.

Applied to files:

  • ee/packages/federation-matrix/src/setup.ts
  • ee/packages/federation-matrix/src/FederationMatrix.ts
🧬 Code graph analysis (2)
ee/packages/federation-matrix/src/setup.ts (3)
ee/packages/federation-matrix/src/index.ts (1)
  • setupFederationMatrix (9-9)
packages/core-services/src/index.ts (1)
  • License (162-162)
ee/packages/federation-matrix/src/events/index.ts (1)
  • registerEvents (11-22)
ee/packages/federation-matrix/src/FederationMatrix.ts (1)
ee/packages/federation-matrix/src/services/MatrixMediaService.ts (1)
  • MatrixMediaService (18-151)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
apps/meteor/ee/server/startup/federation.ts (1)

17-21: Stop typing events are reported as "still typing"

activity.includes('user-typing') returns true for both the start ('user-typing') and stop ('user-typing-off'/'user-typing-stopped') notifications, so Matrix peers never receive the stop signal. Remote clients keep showing the user typing indefinitely.

Apply this diff to fix:

-		void FederationMatrixService.notifyUserTyping(rid, user, activity.includes('user-typing'));
+		const isTyping = activity === 'user-typing';
+		void FederationMatrixService.notifyUserTyping(rid, user, isTyping);
🧹 Nitpick comments (2)
apps/meteor/ee/server/startup/federation.ts (2)

1-2: Consider clearer import aliasing to avoid confusion.

Both imports use FederationMatrix as the identifier (one aliased, one not). While line 1's alias resolves the immediate name collision, the setup is subtle: new FederationMatrix() (line 14) refers to the @rocket.chat/federation-matrix class, while FederationMatrixService (line 21) refers to the service proxy from @rocket.chat/core-services. Consider aliasing both for clarity.

-import { api, FederationMatrix as FederationMatrixService } from '@rocket.chat/core-services';
-import { FederationMatrix, setupFederationMatrix } from '@rocket.chat/federation-matrix';
+import { api, FederationMatrix as FederationMatrixServiceProxy } from '@rocket.chat/core-services';
+import { FederationMatrix as FederationMatrixClass, setupFederationMatrix } from '@rocket.chat/federation-matrix';

Then update usage on lines 14 and 21:

api.registerService(new FederationMatrixClass());
// ...
void FederationMatrixServiceProxy.notifyUserTyping(rid, user, activity.includes('user-typing'));

16-16: Consider moving event handler to service initialization.

The TODO suggests relocating the StreamerCentral listener to the service or setup module, which would improve separation of concerns and make the startup function cleaner.

Would you like me to help refactor this event handler into the federation-matrix setup module?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 345e5c4 and 5db68a5.

📒 Files selected for processing (1)
  • apps/meteor/ee/server/startup/federation.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-19T15:15:04.642Z
Learnt from: rodrigok
PR: RocketChat/Rocket.Chat#36991
File: apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts:219-221
Timestamp: 2025-09-19T15:15:04.642Z
Learning: The Federation_Matrix_homeserver_domain setting in apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts is part of the old federation system and is being deprecated/removed, so configuration issues with this setting should not be flagged for improvement.

Applied to files:

  • apps/meteor/ee/server/startup/federation.ts
🧬 Code graph analysis (1)
apps/meteor/ee/server/startup/federation.ts (4)
ee/packages/federation-matrix/src/setup.ts (1)
  • setupFederationMatrix (36-104)
packages/models/src/index.ts (1)
  • InstanceStatus (164-164)
ee/packages/federation-matrix/src/FederationMatrix.ts (1)
  • FederationMatrix (27-886)
apps/meteor/ee/server/api/federation.ts (1)
  • registerFederationRoutes (43-52)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: 🔨 Test Storybook / Test Storybook
  • GitHub Check: 🔨 Test Unit / Unit Tests
  • GitHub Check: 🔎 Code Check / Code Lint
  • GitHub Check: 🔎 Code Check / TypeScript
  • GitHub Check: 📦 Meteor Build - coverage
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (3)
apps/meteor/ee/server/startup/federation.ts (3)

12-12: LGTM!

The call to setupFederationMatrix(InstanceStatus.id()) correctly passes the instance identifier. Error propagation to the caller is appropriate here.


14-14: LGTM!

Service registration follows setup and correctly instantiates the FederationMatrix class.


25-29: Verify partial startup behavior is intended.

The try/catch logs route registration errors but does not rethrow, allowing startFederationService to complete successfully even when routes fail to register. This means the federation service instance is registered (line 14) and typing events are wired (line 17), but Matrix HTTP endpoints may be unavailable. Confirm this graceful degradation is the desired behavior.

If route registration should be critical, rethrow the error:

 	try {
 		await registerFederationRoutes();
 	} catch (error) {
 		logger.error('Failed to start federation-matrix service:', error);
+		throw error;
 	}

rodrigok
rodrigok previously approved these changes Oct 1, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
ee/packages/federation-matrix/src/FederationMatrix.ts (3)

85-93: Guard presence updates until homeserver services are ready

When the settings watcher flips processEDUPresence to true before getAllServices() finishes (or when the homeserver module is unavailable), this path dereferences this.homeserverServices, throwing and breaking the event loop. Please bail out when the services aren’t ready.

-		void this.homeserverServices.edu.sendPresenceUpdateToRooms(
+		const services = this.homeserverServices;
+		if (!services) {
+			this.logger.warn('Skipping presence EDU: homeserver services not initialized');
+			return;
+		}
+
+		void services.edu.sendPresenceUpdateToRooms(

257-279: Fix remote member lookup when ensuring bridged users

This block compares federation.mui against memberId (the Mongo _id), so it never finds existing remote users and inserts duplicates whose username/federation.mui are set to that _id. That corrupts the user directory and breaks future lookups.

-					let memberId: string | undefined;
+					let memberId: string | undefined;
 
 					if (typeof member === 'string') {
 						memberMatrixUserId = member.startsWith('@') ? member : `@${member}`;
 						memberId = undefined;
 					} else if (member.username?.includes(':')) {
 						memberMatrixUserId = member.username.startsWith('@') ? member.username : `@${member.username}`;
 						memberId = member._id;
 					} else {
 						continue;
 					}
@@
-					if (memberId) {
-						const existingMemberMatrixUserId = await Users.findOne({ 'federation.mui': memberId });
-						if (!existingMemberMatrixUserId) {
+					if (memberId) {
+						const matrixUserId = memberMatrixUserId;
+						const existingMemberMatrixUser = await Users.findOne({ 'federation.mui': matrixUserId });
+						if (!existingMemberMatrixUser) {
 							const newUser = {
-								username: memberId,
-								name: memberId,
+								username: matrixUserId,
+								name: typeof member === 'string' ? matrixUserId : member.username || matrixUserId,
 								type: 'user' as const,
 								status: UserStatus.OFFLINE,
 								active: true,
 								roles: ['user'],
 								requirePasswordChange: false,
 								federated: true,
 								federation: {
 									version: 1,
-									mui: memberId,
-									origin: memberMatrixUserId.split(':').pop(),
+									mui: matrixUserId,
+									origin: matrixUserId.split(':').pop(),
 								},
 								createdAt: new Date(),
 								_updatedAt: new Date(),
 							};
 
 							await Users.insertOne(newUser);

815-838: Typing EDU path needs the same homeserver guard

If typing EDUs are enabled while the homeserver services aren’t initialized, this dereference will throw just like the presence handler. Please guard it the same way.

-		void this.homeserverServices.edu.sendTypingNotification(room.federation.mrid, userMui, isTyping);
+		const services = this.homeserverServices;
+		if (!services) {
+			this.logger.warn('Skipping typing EDU: homeserver services not initialized');
+			return;
+		}
+
+		void services.edu.sendTypingNotification(room.federation.mrid, userMui, isTyping);
♻️ Duplicate comments (2)
ee/packages/federation-matrix/src/setup.ts (2)

52-55: Still need a safe fallback when parsing the MongoDB name

This path throws on malformed MONGO_URL and quietly produces an empty string when the URL omits the DB segment (e.g., mongodb://host). Please add the try/catch + fallback we discussed earlier so we either use a sane default (like 'meteor') or abort with a clear error.


83-88: enableThumbnails flag is inverted

process.env.MEDIA_ENABLE_THUMBNAILS !== 'true' makes the feature false when the env var is explicitly 'true', and still true when it’s 'false'. Please flip the condition so 'true' enables and 'false' disables (defaulting to true when unset).

-			enableThumbnails: process.env.MEDIA_ENABLE_THUMBNAILS !== 'true',
+			enableThumbnails: process.env.MEDIA_ENABLE_THUMBNAILS !== 'false',
🧹 Nitpick comments (1)
packages/models/src/models/Settings.ts (1)

1-1: Require explicit <T> parameters for getValueById at call sites

  • In OAuth2Client.getUserAuthorizationUrl, change
    const clientId = await …getValueById(`${this.config.alias}-oauth-client-id`);
    to
    const clientId = await …getValueById<string>(`${this.config.alias}-oauth-client-id`);
  • In OAuth2Client.refreshUserAccessToken, likewise use
    getValueById<string>(…) for both client-ID and client-secret.

This ensures the returned value is correctly typed as string rather than the broad SettingValue union.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between cec935a and 93c3395.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (18)
  • apps/meteor/ee/server/api/federation.ts (2 hunks)
  • apps/meteor/ee/server/startup/federation.ts (1 hunks)
  • apps/meteor/package.json (0 hunks)
  • ee/apps/federation-service/.eslintrc.json (0 hunks)
  • ee/apps/federation-service/CHANGELOG.md (0 hunks)
  • ee/apps/federation-service/package.json (0 hunks)
  • ee/apps/federation-service/src/config.ts (0 hunks)
  • ee/apps/federation-service/src/service.ts (0 hunks)
  • ee/apps/federation-service/tsconfig.json (0 hunks)
  • ee/packages/federation-matrix/package.json (1 hunks)
  • ee/packages/federation-matrix/src/FederationMatrix.ts (5 hunks)
  • ee/packages/federation-matrix/src/api/routes.ts (1 hunks)
  • ee/packages/federation-matrix/src/index.ts (1 hunks)
  • ee/packages/federation-matrix/src/setup.ts (1 hunks)
  • ee/packages/federation-matrix/tsconfig.json (1 hunks)
  • packages/core-services/src/types/IFederationMatrixService.ts (0 hunks)
  • packages/model-typings/src/models/ISettingsModel.ts (2 hunks)
  • packages/models/src/models/Settings.ts (2 hunks)
💤 Files with no reviewable changes (8)
  • ee/apps/federation-service/CHANGELOG.md
  • ee/apps/federation-service/src/config.ts
  • ee/apps/federation-service/tsconfig.json
  • ee/apps/federation-service/.eslintrc.json
  • ee/apps/federation-service/package.json
  • packages/core-services/src/types/IFederationMatrixService.ts
  • ee/apps/federation-service/src/service.ts
  • apps/meteor/package.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • ee/packages/federation-matrix/package.json
  • packages/model-typings/src/models/ISettingsModel.ts
  • ee/packages/federation-matrix/src/index.ts
  • ee/packages/federation-matrix/tsconfig.json
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: rodrigok
PR: RocketChat/Rocket.Chat#36991
File: apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts:219-221
Timestamp: 2025-09-19T15:15:04.642Z
Learning: The Federation_Matrix_homeserver_domain setting in apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts is part of the old federation system and is being deprecated/removed, so configuration issues with this setting should not be flagged for improvement.
📚 Learning: 2025-09-19T15:15:04.642Z
Learnt from: rodrigok
PR: RocketChat/Rocket.Chat#36991
File: apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts:219-221
Timestamp: 2025-09-19T15:15:04.642Z
Learning: The Federation_Matrix_homeserver_domain setting in apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts is part of the old federation system and is being deprecated/removed, so configuration issues with this setting should not be flagged for improvement.

Applied to files:

  • ee/packages/federation-matrix/src/setup.ts
  • apps/meteor/ee/server/startup/federation.ts
  • ee/packages/federation-matrix/src/FederationMatrix.ts
  • apps/meteor/ee/server/api/federation.ts
📚 Learning: 2025-10-01T12:20:13.136Z
Learnt from: sampaiodiego
PR: RocketChat/Rocket.Chat#37110
File: apps/meteor/ee/server/startup/federation.ts:17-21
Timestamp: 2025-10-01T12:20:13.136Z
Learning: In Rocket.Chat's StreamerCentral broadcast for 'notify-room' events with 'user-activity', the `activity` argument is a string (e.g., 'user-typing') when a user starts typing and an empty array when the user stops typing.

Applied to files:

  • apps/meteor/ee/server/startup/federation.ts
🧬 Code graph analysis (5)
ee/packages/federation-matrix/src/setup.ts (3)
ee/packages/federation-matrix/src/index.ts (1)
  • setupFederationMatrix (9-9)
packages/core-services/src/index.ts (1)
  • License (162-162)
ee/packages/federation-matrix/src/events/index.ts (1)
  • registerEvents (11-22)
ee/packages/federation-matrix/src/api/routes.ts (12)
ee/packages/federation-matrix/src/api/middlewares/isFederationEnabled.ts (1)
  • isFederationEnabledMiddleware (4-9)
ee/packages/federation-matrix/src/api/middlewares/isLicenseEnabled.ts (1)
  • isLicenseEnabledMiddleware (4-9)
ee/packages/federation-matrix/src/api/_matrix/key/server.ts (1)
  • getKeyServerRoutes (35-56)
ee/packages/federation-matrix/src/api/_matrix/versions.ts (1)
  • getFederationVersionsRoutes (28-54)
ee/packages/federation-matrix/src/api/middlewares/isFederationDomainAllowed.ts (1)
  • isFederationDomainAllowedMiddleware (36-61)
ee/packages/federation-matrix/src/api/_matrix/invite.ts (1)
  • getMatrixInviteRoutes (295-351)
ee/packages/federation-matrix/src/api/_matrix/profiles.ts (1)
  • getMatrixProfilesRoutes (352-499)
ee/packages/federation-matrix/src/api/_matrix/rooms.ts (1)
  • getMatrixRoomsRoutes (124-207)
ee/packages/federation-matrix/src/api/_matrix/send-join.ts (1)
  • getMatrixSendJoinRoutes (224-250)
ee/packages/federation-matrix/src/api/_matrix/transactions.ts (1)
  • getMatrixTransactionsRoutes (316-506)
ee/packages/federation-matrix/src/api/_matrix/media.ts (1)
  • getMatrixMediaRoutes (76-153)
ee/packages/federation-matrix/src/api/.well-known/server.ts (1)
  • getWellKnownRoutes (22-46)
apps/meteor/ee/server/startup/federation.ts (3)
ee/packages/federation-matrix/src/setup.ts (1)
  • setupFederationMatrix (36-104)
ee/packages/federation-matrix/src/FederationMatrix.ts (1)
  • FederationMatrix (27-886)
apps/meteor/ee/server/api/federation.ts (1)
  • registerFederationRoutes (43-52)
ee/packages/federation-matrix/src/FederationMatrix.ts (2)
packages/core-typings/src/IUser.ts (1)
  • IUser (186-255)
ee/packages/federation-matrix/src/services/MatrixMediaService.ts (1)
  • MatrixMediaService (18-151)
apps/meteor/ee/server/api/federation.ts (3)
ee/packages/federation-matrix/src/FederationMatrix.ts (1)
  • FederationMatrix (27-886)
ee/packages/federation-matrix/src/index.ts (2)
  • FederationMatrix (3-3)
  • getFederationRoutes (7-7)
ee/packages/federation-matrix/src/api/routes.ts (1)
  • getFederationRoutes (17-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: Builds matrix rust bindings against alpine
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build

Comment on lines 12 to 29
await setupFederationMatrix(InstanceStatus.id());

await api.destroyService(federationMatrixService);
federationMatrixService = undefined;
};
api.registerService(new FederationMatrix());

if (shouldStartService()) {
await startService();
}

void License.onLicense('federation', async () => {
logger.debug('Federation license became available');
if (shouldStartService()) {
await startService();
// TODO move to service/setup?
StreamerCentral.on('broadcast', (name, eventName, args) => {
if (name === 'notify-room' && eventName.endsWith('user-activity')) {
const [rid] = eventName.split('/');
const [user, activity] = args;
void FederationMatrixService.notifyUserTyping(rid, user, activity.includes('user-typing'));
}
});

License.onInvalidateLicense(async () => {
logger.debug('License invalidated, checking federation module');
if (!shouldStartService()) {
await stopService();
}
});

settings.watch('Federation_Service_Enabled', async (enabled) => {
logger.debug('Federation_Service_Enabled setting changed:', enabled);
if (shouldStartService()) {
await startService();
} else {
await stopService();
}
});

settings.watch<string>('Federation_Service_Domain', async (domain) => {
logger.debug('Federation_Service_Domain setting changed:', domain);
if (shouldStartService()) {
if (domain.toLowerCase() !== federationMatrixService?.getServerName().toLowerCase()) {
await stopService();
}
await startService();
} else {
await stopService();
}
});
try {
await registerFederationRoutes();
} catch (error) {
logger.error('Failed to start federation-matrix service:', error);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't run the rest of startup when federation is disabled

setupFederationMatrix returns before calling createFederationContainer whenever the module is off (no license or the Federation_Service_Enabled flag is false). We still instantiate FederationMatrix and call registerFederationRoutes(). Both paths hit getAllServices(), which throws “Federation container not initialized”, so every non-federated deployment now logs “Failed to start federation-matrix service” during boot. Please short-circuit after setup and skip service/route registration when it reports disabled. For example:

-	await setupFederationMatrix(InstanceStatus.id());
-
-	api.registerService(new FederationMatrix());
+	const isEnabled = await setupFederationMatrix(InstanceStatus.id());
+	if (!isEnabled) {
+		logger.info('Federation service disabled; skipping federation-matrix startup');
+		return;
+	}
+
+	api.registerService(new FederationMatrix());

(This requires setupFederationMatrix to return that boolean.) Based on ee/packages/federation-matrix/src/setup.ts.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/meteor/ee/server/startup/federation.ts around lines 12 to 29,
setupFederationMatrix can return early when federation is disabled, but the
current code continues and instantiates services/routes causing errors; update
the startup to inspect the boolean return value from await
setupFederationMatrix(InstanceStatus.id()) and if it returns false, immediately
return (skip api.registerService, the StreamerCentral broadcast handler, and
registerFederationRoutes calls) so service/route registration only happens when
federation is enabled.

Comment on lines +17 to +38
export const getFederationRoutes = (): { matrix: Router<'/_matrix'>; wellKnown: Router<'/.well-known'> } => {
const homeserverServices = getAllServices();

const matrix = new Router('/_matrix');
const wellKnown = new Router('/.well-known');

matrix
.use(isFederationEnabledMiddleware)
.use(isLicenseEnabledMiddleware)
.use(getKeyServerRoutes(homeserverServices))
.use(getFederationVersionsRoutes(homeserverServices))
.use(isFederationDomainAllowedMiddleware)
.use(getMatrixInviteRoutes(homeserverServices))
.use(getMatrixProfilesRoutes(homeserverServices))
.use(getMatrixRoomsRoutes(homeserverServices))
.use(getMatrixSendJoinRoutes(homeserverServices))
.use(getMatrixTransactionsRoutes(homeserverServices))
.use(getMatrixMediaRoutes(homeserverServices));

wellKnown.use(isFederationEnabledMiddleware).use(isLicenseEnabledMiddleware).use(getWellKnownRoutes(homeserverServices));

return { matrix, wellKnown };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Defensive guard around getAllServices() is missing

getAllServices() throws until createFederationContainer() runs. If the license is disabled or setup fails, this function will now crash the route wiring and prevent the server from booting. Please wrap the call, bail out (or return empty routers), and log a warning instead of letting the exception propagate.

🤖 Prompt for AI Agents
In ee/packages/federation-matrix/src/api/routes.ts around lines 17 to 38,
getAllServices() can throw before createFederationContainer() runs which will
crash route wiring; wrap the call in a try/catch, and on error log a warning
including the error details and return empty Router instances for '/_matrix' and
'/.well-known' (so no further federation route helpers are invoked on an
undefined services object), rather than letting the exception propagate and
prevent server boot.

@ggazzo ggazzo added this to the 7.11.0 milestone Oct 1, 2025
@ggazzo ggazzo merged commit 0b6504b into release-7.11.0 Oct 1, 2025
15 of 17 checks passed
@ggazzo ggazzo deleted the native-federation-ms branch October 1, 2025 23:36
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
ee/packages/federation-matrix/src/setup.ts (1)

50-51: Add error handling for MongoDB URI parsing.

The new URL(mongoUri).pathname.slice(1) call can throw if MONGO_URL is malformed or produce an empty string if the pathname is just "/". This should be wrapped in a try/catch block with validation.

Apply this diff to add error handling:

-	const mongoUri = process.env.MONGO_URL || 'mongodb://localhost:3001/meteor';
-	const dbName = process.env.DATABASE_NAME || new URL(mongoUri).pathname.slice(1);
+	const mongoUri = process.env.MONGO_URL || 'mongodb://localhost:3001/meteor';
+	let dbName = process.env.DATABASE_NAME;
+	if (!dbName) {
+		try {
+			const parsed = new URL(mongoUri);
+			dbName = parsed.pathname.slice(1);
+			if (!dbName) {
+				logger.error('MongoDB URI pathname is empty; defaulting to "meteor"');
+				dbName = 'meteor';
+			}
+		} catch (error) {
+			logger.error('Failed to parse MONGO_URL; defaulting database name to "meteor"', error);
+			dbName = 'meteor';
+		}
+	}
apps/meteor/ee/server/startup/federation.ts (1)

13-22: Move service and route registration after enablement check.

Lines 15 and 17 unconditionally register the federation service and routes before checking isEnabled at line 20. When federation is disabled (no license, feature flag off, or invalid domain), setupFederationMatrix returns false, but the service and routes are already registered. This causes "Federation container not initialized" errors when the registered endpoints are accessed.

Apply this diff to guard registration:

 	const isEnabled = await setupFederationMatrix(InstanceStatus.id());
+	if (!isEnabled) {
+		logger.info('Federation service disabled; skipping federation-matrix startup');
+		return;
+	}
 
 	api.registerService(new FederationMatrix());
 
 	await registerFederationRoutes();
 
-	// only registers the typing listener if the service is enabled
-	if (!isEnabled) {
-		return;
-	}
-
 	// TODO move to service/setup?
 	StreamerCentral.on('broadcast', (name, eventName, args) => {
🧹 Nitpick comments (2)
ee/packages/federation-matrix/src/setup.ts (1)

88-100: Consider checking service enablement before container initialization.

The federation container is initialized (lines 90-95) before checking if the service is enabled (line 97). If federation is disabled (no license, feature flag off, or invalid domain), the function returns false but the container has already been created, which may allocate resources unnecessarily.

Consider reordering to validate enablement first:

-	const eventHandler = new Emitter<HomeserverEventSignatures>();
-
-	await createFederationContainer(
-		{
-			emitter: eventHandler,
-		},
-		config,
-	);
-
 	const serviceEnabled = (await License.hasModule('federation')) && settingEnabled && validateDomain(serverName);
 	if (!serviceEnabled) {
 		return false;
 	}
+
+	const eventHandler = new Emitter<HomeserverEventSignatures>();
+
+	await createFederationContainer(
+		{
+			emitter: eventHandler,
+		},
+		config,
+	);
apps/meteor/ee/server/startup/federation.ts (1)

32-34: Consider whether silent failure is appropriate.

The catch block logs the error but doesn't re-throw or return an error indicator. This means startup failures are silent to the caller. If federation startup failure should be communicated (e.g., for monitoring or alerting), consider re-throwing or returning a status.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 93c3395 and 572d841.

📒 Files selected for processing (2)
  • apps/meteor/ee/server/startup/federation.ts (1 hunks)
  • ee/packages/federation-matrix/src/setup.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-19T15:15:04.642Z
Learnt from: rodrigok
PR: RocketChat/Rocket.Chat#36991
File: apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts:219-221
Timestamp: 2025-09-19T15:15:04.642Z
Learning: The Federation_Matrix_homeserver_domain setting in apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts is part of the old federation system and is being deprecated/removed, so configuration issues with this setting should not be flagged for improvement.

Applied to files:

  • apps/meteor/ee/server/startup/federation.ts
📚 Learning: 2025-10-01T12:20:13.136Z
Learnt from: sampaiodiego
PR: RocketChat/Rocket.Chat#37110
File: apps/meteor/ee/server/startup/federation.ts:17-21
Timestamp: 2025-10-01T12:20:13.136Z
Learning: In Rocket.Chat's StreamerCentral broadcast for 'notify-room' events with 'user-activity', the `activity` argument is a string (e.g., 'user-typing') when a user starts typing and an empty array when the user stops typing.

Applied to files:

  • apps/meteor/ee/server/startup/federation.ts
🧬 Code graph analysis (2)
ee/packages/federation-matrix/src/setup.ts (3)
ee/packages/federation-matrix/src/index.ts (1)
  • setupFederationMatrix (9-9)
packages/core-services/src/index.ts (1)
  • License (162-162)
ee/packages/federation-matrix/src/events/index.ts (1)
  • registerEvents (11-22)
apps/meteor/ee/server/startup/federation.ts (3)
ee/packages/federation-matrix/src/setup.ts (1)
  • setupFederationMatrix (39-108)
packages/models/src/index.ts (1)
  • InstanceStatus (164-164)
apps/meteor/ee/server/api/federation.ts (1)
  • registerFederationRoutes (43-52)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (6)
ee/packages/federation-matrix/src/setup.ts (5)

12-37: LGTM! Domain validation is thorough.

The validation logic correctly handles empty, uppercase, and malformed domain inputs with appropriate error logging.


40-48: LGTM! Settings loading is safe.

All settings are loaded with appropriate type assertions and fallback defaults.


53-79: Configuration setup looks good aside from the thumbnail flag.

The ConfigService construction uses appropriate defaults and proper integer parsing with radix 10. The allowed MIME types and rate limits are sensible.

Also applies to: 81-86


102-105: LGTM! Event registration is correctly gated.

Events are only registered when the service is enabled, with appropriate EDU processing flags.


107-107: LGTM! Return value correctly reflects service state.

The function returns true only when federation is fully enabled and initialized.

apps/meteor/ee/server/startup/federation.ts (1)

24-31: LGTM! Typing notification logic is correct.

The activity.includes('user-typing') check correctly handles both the start event (when activity is the string 'user-typing') and the stop event (when activity is an empty array). Based on learnings.

'audio/mpeg',
'audio/ogg',
],
enableThumbnails: process.env.MEDIA_ENABLE_THUMBNAILS !== 'true',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix inverted boolean logic for thumbnail enablement.

Line 80 uses process.env.MEDIA_ENABLE_THUMBNAILS !== 'true', which means thumbnails are disabled when the environment variable is 'true' and enabled otherwise. This is inverted.

Apply this diff to fix the logic:

-			enableThumbnails: process.env.MEDIA_ENABLE_THUMBNAILS !== 'true',
+			enableThumbnails: process.env.MEDIA_ENABLE_THUMBNAILS !== 'false',

Or for strict opt-in behavior (thumbnails only when explicitly enabled):

-			enableThumbnails: process.env.MEDIA_ENABLE_THUMBNAILS !== 'true',
+			enableThumbnails: process.env.MEDIA_ENABLE_THUMBNAILS === 'true',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
enableThumbnails: process.env.MEDIA_ENABLE_THUMBNAILS !== 'true',
enableThumbnails: process.env.MEDIA_ENABLE_THUMBNAILS !== 'false',
🤖 Prompt for AI Agents
In ee/packages/federation-matrix/src/setup.ts around line 80, the boolean is
inverted: currently enableThumbnails is set using
process.env.MEDIA_ENABLE_THUMBNAILS !== 'true' which disables thumbnails when
the env var is 'true'; change the expression to
process.env.MEDIA_ENABLE_THUMBNAILS === 'true' so thumbnails are enabled only
when the environment variable is explicitly set to 'true'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants