Skip to content

Conversation

@ricardogarim
Copy link
Contributor

@ricardogarim ricardogarim commented Nov 24, 2025

Summary by CodeRabbit

  • New Features

    • Added a federation validation service for outbound operations (domain reachability, room ACL, user existence).
    • SDK now exposes outbound validation APIs for consumers.
    • Integrated outbound validation into invite and direct-message flows.
  • Configuration

    • Added configurable timeouts for federation network and user checks.
  • Bug Fixes / Reliability

    • Standardized federation validation error responses for clearer failures.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 24, 2025

Warning

Rate limit exceeded

@sampaiodiego has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 20 minutes and 20 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between bd88cb4 and 326cb5e.

📒 Files selected for processing (4)
  • packages/federation-sdk/src/services/config.service.ts (2 hunks)
  • packages/federation-sdk/src/services/federation-validation.service.ts (1 hunks)
  • packages/federation-sdk/src/services/invite.service.ts (3 hunks)
  • packages/federation-sdk/src/services/room.service.spec.ts (2 hunks)

Walkthrough

Adds a FederationValidationService and FederationValidationError, exposes them and extractDomainFromId from the package index, wires the validation service into FederationSDK, InviteService, and RoomService, adds federation.validation config schema, and makes checkServerAcl non-private.

Changes

Cohort / File(s) Summary
Public API / SDK
packages/federation-sdk/src/index.ts, packages/federation-sdk/src/sdk.ts
Exported FederationValidationService and FederationValidationError; re-exported extractDomainFromId; FederationSDK accepts federationValidationService and exposes validateOutboundUser(...).
Configuration schema
packages/federation-sdk/src/services/config.service.ts
Added federation?.validation to AppConfig and AppConfigSchema with networkCheckTimeoutMs (default 5000ms, min 1000ms) and userCheckTimeoutMs (default 10000ms, min 1000ms).
New validation service
packages/federation-sdk/src/services/federation-validation.service.ts
New FederationValidationService and FederationValidationError; public methods validateOutboundUser and validateOutboundInvite; private checks for room ACL, domain reachability, user existence; timeout helper and normalized error codes (POLICY_DENIED, CONNECTION_FAILED, USER_NOT_FOUND).
Service integrations & ACL visibility
packages/federation-sdk/src/services/invite.service.ts, packages/federation-sdk/src/services/room.service.ts, packages/federation-sdk/src/services/event-authorization.service.ts
Injected FederationValidationService into InviteService and RoomService and call validateOutboundInvite before external invites/DMs; changed EventAuthorizationService.checkServerAcl from private to non-private.
Tests / test setup
packages/federation-sdk/src/services/room.service.spec.ts
Test registration of a stubbed FederationValidationService to bypass validation; removed emitter: undefined from init call.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant SDK as FederationSDK
    participant Inv as InviteService
    participant Val as FederationValidationService
    participant Auth as EventAuthorizationService
    participant Req as FederationRequestService
    participant State as StateService

    Client->>SDK: inviteUserToRoom(userId, roomId)
    SDK->>Inv: inviteUserToRoom(userId, roomId)
    Inv->>Val: validateOutboundInvite(userId, roomId)

    rect rgb(220,235,250)
      Note over Val: Policy Check
      Val->>State: getRoomLatest(roomId)
      Val->>Auth: checkServerAcl(aclEvent, domain)
    end

    rect rgb(220,235,250)
      Note over Val: Network Check
      Val->>Req: GET /.well-known/...
      Req-->>Val: reachable / timeout
    end

    rect rgb(220,235,250)
      Note over Val: Protocol Check
      Val->>Req: GET /_matrix/profile or user endpoint
      Req-->>Val: user found / not found
    end

    alt Validation Success
      Val-->>Inv: OK
      Inv->>Inv: proceed with invite flow
    else Validation Failure
      Val-->>Inv: FederationValidationError
      Inv-->>Client: Error response
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

  • Review hotspots:
    • packages/federation-sdk/src/services/federation-validation.service.ts — timeout racing, error normalization, and outbound network requests.
    • DI and constructor changes in sdk.ts, invite.service.ts, and room.service.ts — verify wiring and instantiation.
    • event-authorization.service.ts visibility change for checkServerAcl and its consumers.

Possibly related PRs

Suggested reviewers

  • ggazzo

Poem

🐇 I hop through domains with a careful ear,
I knock, I ping, I probe, I peer.
Policy, ping, and presence true—
I stop the hops that break the view.
Thump-thump, a tidy path for you.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'feat: add improved errors messaging' is vague and does not accurately reflect the main changes, which implement pre-federation validation with network, policy, and protocol checks rather than just error messaging improvements. Update title to reflect the primary change: 'feat: implement pre-federation validation for domain and user existence' or similar, accurately describing the validation shield being added.
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.
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from FB-25: adds validation service with policy/network/protocol checks, integrates into invite and room services, returns proper error codes and messages, and enforces sequential validation before outbound transactions.
Out of Scope Changes check ✅ Passed All changes are in scope per FB-25: validation logic, error handling, and integration points are all specified API-level requirements. No out-of-scope UI/UX or cross-domain lookup improvements are present.

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.

@codecov-commenter
Copy link

codecov-commenter commented Nov 24, 2025

Codecov Report

❌ Patch coverage is 29.93197% with 103 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.15%. Comparing base (8955f15) to head (326cb5e).

Files with missing lines Patch % Lines
...-sdk/src/services/federation-validation.service.ts 17.27% 91 Missing ⚠️
packages/federation-sdk/src/sdk.ts 37.50% 5 Missing ⚠️
...ckages/federation-sdk/src/services/room.service.ts 28.57% 5 Missing ⚠️
...on-sdk/src/services/event-authorization.service.ts 0.00% 1 Missing ⚠️
...ages/federation-sdk/src/services/invite.service.ts 87.50% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main     #305    +/-   ##
========================================
  Coverage   53.14%   53.15%            
========================================
  Files          96       97     +1     
  Lines       12798    12942   +144     
========================================
+ Hits         6802     6879    +77     
- Misses       5996     6063    +67     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 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.

@ricardogarim ricardogarim marked this pull request as ready for review November 24, 2025 12:40
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: 3

🧹 Nitpick comments (2)
packages/federation-sdk/src/services/federation-validation.service.ts (2)

72-89: Consider logging the underlying error for troubleshooting.

The catch block (line 82) discards the original error details, making it difficult to diagnose why a domain is unreachable (network timeout, DNS failure, TLS error, etc.).

Add logging to preserve diagnostic information:

-		} catch (_error) {
+		} catch (error) {
+			// Log for debugging while maintaining user-friendly message
+			console.debug('Domain reachability check failed:', { domain, error });
 			throw new FederationValidationError(
 				'CONNECTION_FAILED',
 				'Connection Failed. The server domain could not be reached or does not support federation.',
 				502,
 			);
 		}

91-112: Consider logging the underlying error for troubleshooting.

Similar to checkDomainReachable, the catch block (line 105) discards error details that could help diagnose issues (user not found vs. server error vs. timeout).

Add logging to preserve diagnostic information:

-		} catch (_error) {
+		} catch (error) {
+			// Log for debugging while maintaining user-friendly message
+			console.debug('User existence check failed:', { userId, domain, error });
 			throw new FederationValidationError(
 				'USER_NOT_FOUND',
 				'Invitation blocked. The specified user couldn't be found on their homeserver.',
 				502,
 			);
 		}
📜 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 c3605da and 0899cb7.

📒 Files selected for processing (7)
  • packages/federation-sdk/src/index.ts (2 hunks)
  • packages/federation-sdk/src/sdk.ts (3 hunks)
  • packages/federation-sdk/src/services/config.service.ts (2 hunks)
  • packages/federation-sdk/src/services/event-authorization.service.ts (1 hunks)
  • packages/federation-sdk/src/services/federation-validation.service.ts (1 hunks)
  • packages/federation-sdk/src/services/invite.service.ts (3 hunks)
  • packages/federation-sdk/src/services/room.service.ts (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-10T22:18:31.655Z
Learnt from: sampaiodiego
Repo: RocketChat/homeserver PR: 224
File: packages/federation-sdk/src/services/event-authorization.service.ts:261-268
Timestamp: 2025-10-10T22:18:31.655Z
Learning: In packages/federation-sdk/src/services/state.service.ts, the method `StateService.getLatestRoomState(roomId: string)` has return type `Promise<State>` and never returns undefined. If the state is not found, it throws an error with message "No state found for room ${roomId}" instead of returning undefined.

Applied to files:

  • packages/federation-sdk/src/services/room.service.ts
📚 Learning: 2025-09-14T13:34:12.448Z
Learnt from: ricardogarim
Repo: RocketChat/homeserver PR: 184
File: packages/federation-sdk/src/services/event-authorization.service.ts:213-223
Timestamp: 2025-09-14T13:34:12.448Z
Learning: ACL events (m.room.server_acl) are not yet implemented in the homeserver codebase, so ACL-related code currently handles the simpler case where no ACL events exist in room state.

Applied to files:

  • packages/federation-sdk/src/services/event-authorization.service.ts
🧬 Code graph analysis (1)
packages/federation-sdk/src/services/invite.service.ts (2)
packages/federation-sdk/src/index.ts (1)
  • FederationValidationService (66-66)
packages/room/src/manager/event-wrapper.ts (1)
  • roomId (101-103)
🔇 Additional comments (8)
packages/federation-sdk/src/services/config.service.ts (1)

38-100: LGTM! Configuration schema is well-structured.

The federation validation timeouts are properly configured with sensible defaults (5s network, 10s user check) and minimum bounds (1000ms). The optional validation block enables gradual adoption.

Note: Ensure existing configuration files include at least an empty federation: {} block to avoid schema validation errors, since the federation property is required (not marked optional) in the interface and schema.

packages/federation-sdk/src/index.ts (1)

65-68: LGTM! Public API exports are appropriate.

The new exports for FederationValidationService, FederationValidationError, and extractDomainFromId properly expose the validation functionality to consumers.

Also applies to: 142-142

packages/federation-sdk/src/services/event-authorization.service.ts (1)

213-213: LGTM! Visibility broadening is appropriate.

Changing checkServerAcl from private to public enables FederationValidationService to perform ACL checks during outbound validation, aligning with the pre-federation validation requirements.

Based on learnings, ACL events are not yet fully implemented in the homeserver codebase, so the simpler case handling here is expected.

packages/federation-sdk/src/services/room.service.ts (1)

40-40: LGTM! Dependency injection is properly implemented.

The FederationValidationService is correctly imported and injected into RoomService.

Also applies to: 67-67

packages/federation-sdk/src/sdk.ts (1)

10-10: LGTM! SDK wrapper follows established patterns.

The validateOutboundUser method properly delegates to FederationValidationService and uses the same wrapper pattern as other SDK methods.

Also applies to: 41-41, 208-214

packages/federation-sdk/src/services/invite.service.ts (1)

17-17: LGTM! Validation is properly positioned.

The validateOutboundInvite call is well-placed to fail fast before processing the invite (either locally or remotely), preventing invalid invites from being persisted.

Also applies to: 48-48, 107-110

packages/federation-sdk/src/services/federation-validation.service.ts (2)

114-125: LGTM! Timeout helper is correctly implemented.

The withTimeout method properly races the operation against a timeout. Note that JavaScript Promises don't support native cancellation, so the underlying operation continues after timeout (this is expected Promise behavior, not a bug).

For HTTP requests, this is acceptable behavior since most client libraries handle aborted connections gracefully.


33-44: No action required—error handling is already properly implemented.

The extractDomainFromId function already validates the identifier format and throws a clear error ("Invalid identifier {identifier}, no domain found") when the domain cannot be extracted. The error propagates to the calling validation methods, causing validation to fail gracefully as intended.

Likely an incorrect or invalid review comment.

Comment on lines 46 to 62
private async checkRoomAcl(roomId: RoomID, domain: string): Promise<void> {
try {
const state = await this.stateService.getLatestRoomState(roomId);
const aclEvent = state.get('m.room.server_acl:');
if (!aclEvent || !aclEvent.isServerAclEvent()) {
return;
}

const isAllowed = await this.eventAuthorizationService.checkServerAcl(
aclEvent,
domain,
);
if (!isAllowed) {
throw new FederationValidationError(
'POLICY_DENIED',
"Action Blocked. The room's access control policy blocks communication with this domain.",
403,
);
}
} catch (error) {
if (error instanceof FederationValidationError) {
throw 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 | 🔴 Critical

Critical: Fix the error handling in ACL check.

The catch block (lines 65-69) silently swallows non-FederationValidationError exceptions, causing the ACL check to fail open. If getLatestRoomState or checkServerAcl throw unexpected errors, the validation succeeds inappropriately.

This is a security risk: ACL checks must fail closed. Any error should block the operation.

Apply this diff to fail closed on errors:

 	private async checkRoomAcl(roomId: RoomID, domain: string): Promise<void> {
 		try {
 			const state = await this.stateService.getLatestRoomState(roomId);
 			const aclEvent = state.get('m.room.server_acl:');
 			if (!aclEvent || !aclEvent.isServerAclEvent()) {
 				return;
 			}
 
 			const isAllowed = await this.eventAuthorizationService.checkServerAcl(
 				aclEvent,
 				domain,
 			);
 			if (!isAllowed) {
 				throw new FederationValidationError(
 					'POLICY_DENIED',
 					"Action Blocked. The room's access control policy blocks communication with this domain.",
 					403,
 				);
 			}
 		} catch (error) {
 			if (error instanceof FederationValidationError) {
 				throw error;
 			}
+			// Fail closed on unexpected errors
+			throw new FederationValidationError(
+				'POLICY_DENIED',
+				"Action Blocked. Unable to verify room access policy.",
+				403,
+			);
 		}
 	}
📝 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
private async checkRoomAcl(roomId: RoomID, domain: string): Promise<void> {
try {
const state = await this.stateService.getLatestRoomState(roomId);
const aclEvent = state.get('m.room.server_acl:');
if (!aclEvent || !aclEvent.isServerAclEvent()) {
return;
}
const isAllowed = await this.eventAuthorizationService.checkServerAcl(
aclEvent,
domain,
);
if (!isAllowed) {
throw new FederationValidationError(
'POLICY_DENIED',
"Action Blocked. The room's access control policy blocks communication with this domain.",
403,
);
}
} catch (error) {
if (error instanceof FederationValidationError) {
throw error;
}
}
}
private async checkRoomAcl(roomId: RoomID, domain: string): Promise<void> {
try {
const state = await this.stateService.getLatestRoomState(roomId);
const aclEvent = state.get('m.room.server_acl:');
if (!aclEvent || !aclEvent.isServerAclEvent()) {
return;
}
const isAllowed = await this.eventAuthorizationService.checkServerAcl(
aclEvent,
domain,
);
if (!isAllowed) {
throw new FederationValidationError(
'POLICY_DENIED',
"Action Blocked. The room's access control policy blocks communication with this domain.",
403,
);
}
} catch (error) {
if (error instanceof FederationValidationError) {
throw error;
}
// Fail closed on unexpected errors
throw new FederationValidationError(
'POLICY_DENIED',
"Action Blocked. Unable to verify room access policy.",
403,
);
}
}
🤖 Prompt for AI Agents
In packages/federation-sdk/src/services/federation-validation.service.ts around
lines 46 to 70, the current catch block swallows unexpected errors and lets ACL
checks fail open; change it so any caught non-FederationValidationError causes
the check to fail closed by throwing a FederationValidationError. Specifically,
keep rethrowing FederationValidationError instances as-is, but for any other
error throw a new FederationValidationError('POLICY_DENIED', "Action Blocked.
The room's access control policy blocks communication with this domain." , 403)
— optionally include the original error message/details in the new error for
diagnostics.

Comment on lines 1522 to +1649
if (isExternalUser) {
await this.federationValidationService.validateOutboundInvite(
targetUserId,
roomCreateEvent.roomId,
);

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 | 🟡 Minor

🧩 Analysis chain

Consider the room cleanup implications.

The validation occurs after the DM room has been fully created and persisted (lines 1406-1522). If validateOutboundInvite fails, the room remains in the database with only the creator as a member.

This design trade-off exists because the ACL check requires room state. However, it could lead to orphaned single-member rooms accumulating over time. Consider:

  • Whether orphaned rooms are acceptable for your use case
  • Adding cleanup logic to remove the room if validation fails
  • Documenting this behavior

🏁 Script executed:

#!/bin/bash
# Check if there's any cleanup logic for failed room creation elsewhere
rg -nP --type=ts -C3 'createDirectMessageRoom.*catch|try.*createDirectMessageRoom' 

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Find the createDirectMessageRoom method implementation
ast-grep --pattern $'$$$createDirectMessageRoom($$$) {
  $$$
}'

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for validateOutboundInvite to understand what it does
rg -nP --type=ts -A10 'validateOutboundInvite\s*\(' -g '!node_modules'

Length of output: 3203


🏁 Script executed:

#!/bin/bash
# Check the broader createDirectMessageRoom implementation for transaction or error handling
rg -nP --type=ts -B5 -A30 'createDirectMessageRoom.*\{' packages/federation-sdk/src/services/room.service.ts | head -80

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Find who calls createDirectMessageRoom
rg -nP --type=ts 'createDirectMessageRoom\s*\(' -B3 -A3 -g '!node_modules' | grep -v test

Length of output: 2735


🏁 Script executed:

#!/bin/bash
# Check what errors checkDomainReachable and checkUserExists can throw
rg -nP --type=ts -A15 'private async checkDomainReachable\s*\(|private async checkUserExists\s*\(' packages/federation-sdk/src/services/federation-validation.service.ts

Length of output: 1304


🏁 Script executed:

#!/bin/bash
# Check if the entire createDirectMessageRoom is wrapped in a transaction or has error handling
rg -nP --type=ts -B10 'isExternalUser' packages/federation-sdk/src/services/room.service.ts | grep -A10 -B10 'isExternalUser.*{'

Length of output: 290


🏁 Script executed:

#!/bin/bash
# Get the full try-catch block from direct-message.controller.ts
rg -nP --type=ts -B5 -A20 'createDirectMessageRoom' packages/homeserver/src/controllers/internal/direct-message.controller.ts

Length of output: 721


🏁 Script executed:

#!/bin/bash
# Check what type of error response is returned
rg -nP --type=ts -A30 'catch.*error' packages/homeserver/src/controllers/internal/direct-message.controller.ts

Length of output: 701


Add cleanup logic or validate external user before room creation.

The concern is confirmed: if validateOutboundInvite fails (e.g., domain unreachable, user not found), the DM room persists in the database with only the creator as a member. The controller's error handler (direct-message.controller.ts:38-44) returns an error response but does not remove the orphaned room.

While the validation must occur after room creation (because the ACL check requires room state), consider:

  • Moving domain/user validation checks before room creation, keeping only the ACL check after
  • Adding transaction rollback or explicit cleanup if validation fails
  • Documenting this behavior if orphaned rooms are acceptable

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

🧹 Nitpick comments (1)
packages/federation-sdk/src/services/config.service.ts (1)

85-100: Schema must match interface if federation becomes optional.

The schema currently requires the federation property (no .optional() modifier), which is consistent with the interface. However, if the interface is updated to make federation optional (as suggested above), the schema must also be updated accordingly.

If you make federation optional in the interface, apply this diff to match the schema:

-	federation: z.object({
+	federation: z.object({
 		validation: z
 			.object({
 				networkCheckTimeoutMs: z
 					.number()
 					.int()
 					.min(1000, 'Network check timeout must be at least 1000ms')
 					.default(5000),
 				userCheckTimeoutMs: z
 					.number()
 					.int()
 					.min(1000, 'User check timeout must be at least 1000ms')
 					.default(10000),
 			})
 			.optional(),
-	}),
+	}).optional(),

Otherwise, the timeout constraints and error messages look good.

📜 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 0899cb7 and e3c120d.

📒 Files selected for processing (7)
  • packages/federation-sdk/src/index.ts (2 hunks)
  • packages/federation-sdk/src/sdk.ts (3 hunks)
  • packages/federation-sdk/src/services/config.service.ts (2 hunks)
  • packages/federation-sdk/src/services/event-authorization.service.ts (1 hunks)
  • packages/federation-sdk/src/services/federation-validation.service.ts (1 hunks)
  • packages/federation-sdk/src/services/invite.service.ts (3 hunks)
  • packages/federation-sdk/src/services/room.service.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/federation-sdk/src/index.ts
  • packages/federation-sdk/src/services/invite.service.ts
  • packages/federation-sdk/src/services/federation-validation.service.ts
  • packages/federation-sdk/src/sdk.ts
  • packages/federation-sdk/src/services/event-authorization.service.ts
  • packages/federation-sdk/src/services/room.service.ts
🔇 Additional comments (1)
packages/federation-sdk/src/services/config.service.ts (1)

38-43: federation property is now required and will break existing production configurations without it.

The setConfig() method validates configuration using AppConfigSchema.parse() in production (when NODE_ENV !== 'test'). The test at lines 74-105 explicitly validates that configurations missing the federation property throw an error matching /federation|required/i. Both the interface definition (lines 38-43) and Zod schema (lines 85-100) make federation required.

Tests bypass this validation by checking NODE_ENV === 'test', which is why test cases without federation don't fail.

Any existing deployment with a configuration missing the federation property will fail at runtime. Confirm whether:

  1. All existing configurations already include federation, or
  2. This breaking change is intentional with a documented migration path

If backward compatibility is needed, make federation optional:

-	federation: {
+	federation?: {
 		validation?: {
 			networkCheckTimeoutMs?: number;
 			userCheckTimeoutMs?: number;
 		};
 	};

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

🧹 Nitpick comments (2)
packages/federation-sdk/src/services/federation-validation.service.ts (2)

64-80: Consider logging original errors for debugging.

The catch block swallows the original error, which may contain valuable debugging information (e.g., network timeout, DNS failure, HTTP status codes). While the user-facing message is appropriately generic, logging the original error would aid troubleshooting.

🔎 Suggested enhancement

Add a logger and log the original error:

+import { createLogger } from '@rocket.chat/federation-core';
+
 @singleton()
 export class FederationValidationService {
+	private readonly logger = createLogger('FederationValidationService');
+
 	constructor(
 		private readonly configService: ConfigService,
 		private readonly federationRequestService: FederationRequestService,
 		private readonly stateService: StateService,
 		private readonly eventAuthorizationService: EventAuthorizationService,
 	) {}

Then in checkDomainReachable:

-	} catch (_error) {
+	} catch (error) {
+		this.logger.warn({
+			msg: 'Domain reachability check failed',
+			domain,
+			error,
+		});
 		throw new FederationValidationError(

82-102: Consider logging original errors for debugging.

Similar to checkDomainReachable, this method swallows the original error. Logging would help diagnose whether failures are due to network issues, 404 responses, or other causes.

🔎 Suggested enhancement
-	} catch (_error) {
+	} catch (error) {
+		this.logger.warn({
+			msg: 'User existence check failed',
+			userId,
+			domain,
+			error,
+		});
 		throw new FederationValidationError(
📜 Review details

Configuration used: Organization 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 e3c120d and 7e52690.

📒 Files selected for processing (7)
  • packages/federation-sdk/src/index.ts (2 hunks)
  • packages/federation-sdk/src/sdk.ts (3 hunks)
  • packages/federation-sdk/src/services/config.service.ts (2 hunks)
  • packages/federation-sdk/src/services/event-authorization.service.ts (1 hunks)
  • packages/federation-sdk/src/services/federation-validation.service.ts (1 hunks)
  • packages/federation-sdk/src/services/invite.service.ts (3 hunks)
  • packages/federation-sdk/src/services/room.service.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/federation-sdk/src/services/room.service.ts
  • packages/federation-sdk/src/services/invite.service.ts
  • packages/federation-sdk/src/services/config.service.ts
  • packages/federation-sdk/src/sdk.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-10T22:18:31.655Z
Learnt from: sampaiodiego
Repo: RocketChat/homeserver PR: 224
File: packages/federation-sdk/src/services/event-authorization.service.ts:261-268
Timestamp: 2025-10-10T22:18:31.655Z
Learning: In packages/federation-sdk/src/services/state.service.ts, the method `StateService.getLatestRoomState(roomId: string)` has return type `Promise<State>` and never returns undefined. If the state is not found, it throws an error with message "No state found for room ${roomId}" instead of returning undefined.

Applied to files:

  • packages/federation-sdk/src/services/federation-validation.service.ts
📚 Learning: 2025-09-14T13:34:12.448Z
Learnt from: ricardogarim
Repo: RocketChat/homeserver PR: 184
File: packages/federation-sdk/src/services/event-authorization.service.ts:213-223
Timestamp: 2025-09-14T13:34:12.448Z
Learning: ACL events (m.room.server_acl) are not yet implemented in the homeserver codebase, so ACL-related code currently handles the simpler case where no ACL events exist in room state.

Applied to files:

  • packages/federation-sdk/src/services/federation-validation.service.ts
  • packages/federation-sdk/src/services/event-authorization.service.ts
🧬 Code graph analysis (1)
packages/federation-sdk/src/services/federation-validation.service.ts (3)
packages/federation-sdk/src/index.ts (2)
  • FederationValidationError (68-68)
  • FederationValidationService (67-67)
packages/room/src/manager/event-wrapper.ts (2)
  • roomId (111-113)
  • reject (458-462)
packages/federation-sdk/src/specs/federation-api.ts (1)
  • FederationEndpoints (24-71)
🔇 Additional comments (7)
packages/federation-sdk/src/services/event-authorization.service.ts (1)

214-214: LGTM! Visibility change enables external validation.

Making checkServerAcl public allows FederationValidationService to reuse ACL enforcement logic, supporting the outbound validation workflow introduced in this PR.

packages/federation-sdk/src/index.ts (2)

66-69: LGTM! Public API expanded appropriately.

Exposing FederationValidationService and FederationValidationError enables consumers to perform pre-federation validation and handle validation errors consistently.


143-143: LGTM! Utility export supports validation workflows.

Exposing extractDomainFromId provides a reusable utility for domain extraction during outbound validation.

packages/federation-sdk/src/services/federation-validation.service.ts (4)

10-21: LGTM! Clean error class design.

The FederationValidationError provides consistent error handling with clear error codes and user-friendly messages. The error field derivation from code ensures consistency.


32-36: LGTM! Clear validation sequence.

The validateOutboundUser method follows a logical flow: domain extraction → reachability check → user existence check. This fail-fast approach aligns with the PR objectives.


38-43: LGTM! Prioritizes ACL checks appropriately.

The validateOutboundInvite method correctly checks room ACL before performing network calls, failing fast on policy violations and avoiding unnecessary external requests.


104-115: LGTM! Standard timeout implementation.

The withTimeout helper correctly races the promise against a timeout, providing predictable timeout behavior for validation checks.

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

🧹 Nitpick comments (1)
packages/federation-sdk/src/services/room.service.spec.ts (1)

46-56: Fix typo and consider type-safe mock pattern.

The validation bypass is appropriate for testing core RoomService functionality in isolation. However, consider these improvements:

  1. Typo: "dont" → "don't" on line 46
  2. Type safety: The as unknown as FederationValidationService assertion bypasses type checking. If the interface evolves, this mock won't reflect changes. Consider creating a proper mock class or using a type-safe approach:
// Type-safe alternative
const mockValidationService: Pick<FederationValidationService, 'validateOutboundUser' | 'validateOutboundInvite'> = {
    async validateOutboundUser() {
        return true;
    },
    async validateOutboundInvite() {
        return true;
    },
};

container.register(FederationValidationService, {
    useValue: mockValidationService as FederationValidationService,
});

Note: The Codecov report indicates federation-validation.service.ts has only 17.27% coverage. While bypassing validation here is correct for RoomService tests, ensure the validation logic itself has dedicated test coverage.

📜 Review details

Configuration used: Organization 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 7e52690 and bd88cb4.

📒 Files selected for processing (2)
  • packages/federation-sdk/src/services/invite.service.ts (3 hunks)
  • packages/federation-sdk/src/services/room.service.spec.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/federation-sdk/src/services/invite.service.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-10T22:18:31.655Z
Learnt from: sampaiodiego
Repo: RocketChat/homeserver PR: 224
File: packages/federation-sdk/src/services/event-authorization.service.ts:261-268
Timestamp: 2025-10-10T22:18:31.655Z
Learning: In packages/federation-sdk/src/services/state.service.ts, the method `StateService.getLatestRoomState(roomId: string)` has return type `Promise<State>` and never returns undefined. If the state is not found, it throws an error with message "No state found for room ${roomId}" instead of returning undefined.

Applied to files:

  • packages/federation-sdk/src/services/room.service.spec.ts
🧬 Code graph analysis (1)
packages/federation-sdk/src/services/room.service.spec.ts (1)
packages/federation-sdk/src/index.ts (1)
  • FederationValidationService (67-67)
🔇 Additional comments (2)
packages/federation-sdk/src/services/room.service.spec.ts (2)

11-11: LGTM! Import aligns with public API expansion.

The import of FederationValidationService is consistent with its exposure from the package root index, as needed for the test mock registration.


31-34: LGTM! Simplified initialization.

Removing the explicit emitter: undefined simplifies the init call, which is appropriate if the parameter is optional.

@sampaiodiego sampaiodiego merged commit 46b242c into main Dec 19, 2025
3 checks passed
@sampaiodiego sampaiodiego deleted the feat/errors-messaging branch December 19, 2025 22:55
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