feat(federation): add improved errors messaging
Proposed changes (including videos or screenshots)
As per FB-25, this PR implements a pre-federation validation shield that blocks outbound federation actions (invites, DMs, and room creation involving remote users) unless the destination domain is allowed, reachable, and the target user exists.
Works along with https://github.com/RocketChat/homeserver/pull/305.
This introduces a sequential validation layer composed of three checks that run before any federation transaction is executed.
1. Policy vetting check (allow list enforcement)
Validates whether the remote domain is authorized according to the organization's Federation Domain Allow List.
If the domain is not allowed, the server immediately returns:
- HTTP 403 Forbidden
- Error code:
federation-policy-denied
Message returned: "Action Blocked. Communication with one of the domains in the list is restricted by your organization's security policy."
2. Domain existence check (DNS / reachability)
Ensures that the remote homeserver domain resolves and is reachable.
If DNS lookup fails or the server is unreachable, the server returns:
- Error code:
federation-connection-failed
Message: "Connection Failed. The server domain could not be reached or does not support federation."
3. User existence check (protocol lookup)
Queries the remote homeserver for the given user ID.
If the remote server responds with a 404 for the user, the server blocks the request with:
- Error code:
federation-user-not-found
Message: "Invitation blocked. The specified user couldn’t be found on their homeserver."
Issue(s)
Test cases
| Test Category | Scenario | Pathways Tested | Input | Expected Result | Error Code | UI Feedback |
|---|---|---|---|---|---|---|
| General Rule | Invite multiple users where one fails | Room creation modal, DM modal, inside room | Mixed users | Entire operation fails | Depends on failure type | Error message displayed |
POLICY VETTING (RC ALLOW LIST)
| Test Category | Scenario | Pathways Tested | Input | Expected Result | Error Code | UI Feedback |
|---|---|---|---|---|---|---|
| Policy Vetting | RC Allow List contains only hs1, inviting user from hs2 |
Room creation modal | @admin:hs2 |
Fail | federation-policy-denied |
Policy message |
| Policy Vetting | Same case tested via DM creation | DM creation modal | @admin:hs2 |
Fail | Same | Same |
| Policy Vetting | Same case tested via inside-room invite | Inside room ("Continue adding?") | @admin:hs2 |
Status POLICY_DENIED; failing on continue |
federation-policy-denied |
Tooltip and final error |
| Policy Vetting | Same case tested via /invite |
Slash command | /invite @admin:hs2 |
Fail | federation-policy-denied |
Same policy message |
| Policy Vetting | RC Allow List contains only hs1, inviting user from hs1 |
All entry points | @admin:hs1 |
Success | None | None |
| Policy Vetting | Empty allow list allows all domains | All entry points | Any domain | Success | None | None |
DOMAIN EXISTENCE
| Test Category | Scenario | Pathways Tested | Input | Expected Result | Error Code | UI Feedback |
|---|---|---|---|---|---|---|
| Domain Check | Non-existent domain | Room creation modal | @admin:hs3 |
Fail | federation-connection-failed |
Connection failed message |
| Domain Check | Same case via DM creation | DM creation modal | @admin:hs3 |
Fail | Same | Same |
| Domain Check | Same case inside room | Inside room | @admin:hs3 |
Status CONNECTION_FAILED |
None (status-level) + failure on continue | Tooltip: "Unable to verify user" |
| Domain Check | Same case via /invite |
Slash command | /invite @admin:hs3 |
Fail | federation-connection-failed |
Same |
USER EXISTENCE
| Test Category | Scenario | Pathways Tested | Input | Expected Result | Error Code | UI Feedback |
|---|---|---|---|---|---|---|
| User Existence | Non-existent user on valid domain | Room creation modal | @admin2:hs1 |
Fail | federation-user-not-found |
User-not-found message |
| User Existence | Same case via DM creation | DM creation modal | @admin2:hs1 |
Fail | Same | Same |
| User Existence | Same case inside room | Inside room | @admin2:hs1 |
Status USER_NOT_FOUND, continue disabled |
None (status-level) | Tooltip: "User is unverified" |
| User Existence | Same case via /invite |
Slash command | /invite @admin2:hs1 |
Fail | federation-user-not-found |
Same |
Summary by CodeRabbit
-
New Features
- Enforced federation domain allowlist for username-based members and when adding users to rooms.
- Validates federated users before direct room creation with clearer mapped error responses.
- Exposed federation validation helpers and a federation validation error type for federation flows.
-
Chores
- Added configurable federation validation timeouts for network and user checks.
✏️ Tip: You can customize this high-level summary in your review settings.
Looks like this PR is ready to merge! 🎉 If you have any trouble, please check the PR guidelines
⚠️ No Changeset found
Latest commit: decfe3d4d9f4d6de4c782e3b1906f03467a7236d
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
Walkthrough
Adds pre-federation domain allowlist and federated-user validation checks to room creation and invite hooks; exposes new federation-domain helpers and a FederationMatrix.validateFederatedUsers API; surfaces federation validation errors as Meteor errors and adds related configuration and dependency bumps.
Changes
| Cohort / File(s) | Summary |
|---|---|
Room creation & hooks apps/meteor/app/lib/server/functions/createRoom.ts, apps/meteor/ee/server/hooks/federation/index.ts |
Enforce domain allowlist for username-based federated members, validate federated usernames via FederationMatrix.validateFederatedUsers, map FederationValidationError → Meteor.Error, and require access-federation permission where applicable. |
Federation domain allowlist ee/packages/federation-matrix/src/api/middlewares/isFederationDomainAllowed.ts |
New helpers isFederationDomainAllowed(domains) and isFederationDomainAllowedForUsernames(usernames) that extract domains and evaluate against exact, wildcard, and suffix allowlist rules. |
Federation core & API ee/packages/federation-matrix/src/FederationMatrix.ts, ee/packages/federation-matrix/src/index.ts |
Added validateFederatedUsers(usernames: string[]) to FederationMatrix; integrate domain allowlist checks in verification flows; export isFederationDomainAllowed* and FederationValidationError. Maps SDK error codes to validation results. |
Config & timeouts ee/packages/federation-matrix/src/setup.ts |
Adds federation.validation config (networkCheckTimeoutMs, userCheckTimeoutMs) sourced from env vars (defaults 3000ms). |
Service interface & deps packages/core-services/src/types/IFederationMatrixService.ts, ee/packages/federation-matrix/package.json, packages/core-services/package.json |
Added validateFederatedUsers to IFederationMatrixService; bumped @rocket.chat/federation-sdk from 0.3.5 → 0.3.6 in federation and core-services packages. |
Sequence Diagram(s)
sequenceDiagram
participant Client
participant CreateRoom as createRoom Handler
participant FedAPI as isFederationDomainAllowedForUsernames
participant FedMatrix as FederationMatrix.validateFederatedUsers
participant SDK as federation-sdk
Client->>CreateRoom: createRoom(members: usernames[])
CreateRoom->>FedAPI: isFederationDomainAllowedForUsernames(usernames)
alt Domain Allowed
FedAPI-->>CreateRoom: true
CreateRoom->>FedMatrix: validateFederatedUsers(usernames)
FedMatrix->>SDK: validateOutboundUser(matrixId) for each federated user
alt Validation Success
SDK-->>FedMatrix: verified
FedMatrix-->>CreateRoom: success
CreateRoom-->>Client: room created
else Validation Failure
SDK-->>FedMatrix: error (unreachable/not found/policy)
FedMatrix-->>CreateRoom: throw FederationValidationError
CreateRoom-->>Client: Meteor.Error (federation-policy-denied / mapped error)
end
else Domain Blocked
FedAPI-->>CreateRoom: false
CreateRoom-->>Client: Meteor.Error (federation-policy-denied)
end
sequenceDiagram
participant Client
participant Hook as beforeAddUserToRoom
participant Perm as Permission Service
participant FedAPI as isFederationDomainAllowedForUsernames
participant Invite as invite flow
Client->>Hook: add user to room (username)
Hook->>Perm: check access-federation
alt Permitted
Perm-->>Hook: allowed
Hook->>FedAPI: isFederationDomainAllowedForUsernames([username])
alt Domain Allowed
FedAPI-->>Hook: true
Hook->>Invite: proceed to federated invite
Invite-->>Client: success
else Domain Denied
FedAPI-->>Hook: false
Hook-->>Client: Meteor.Error (federation-policy-denied)
end
else Not Permitted
Perm-->>Hook: denied
Hook-->>Client: permission error
end
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
- RocketChat/Rocket.Chat#37582 — touches createDirectMessage and federated flag handling which affects whether new validation gates trigger.
- RocketChat/Rocket.Chat#37015 — modifies the same createRoom flow; likely overlaps with pre-creation federation checks.
- RocketChat/Rocket.Chat#37377 — adjusts federation authorization hooks and permission usage overlapping beforeAddUserToRoom changes.
Suggested labels
stat: ready to merge
Suggested reviewers
- ggazzo
- sampaiodiego
Poem
🐇 I checked each domain and counted hops,
Small ears perked when validation stops.
Domains allowed, users found—hooray!
No ghost rooms left to lead astray.
A hopping cheer for safe DMs today! 🥕
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| 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 (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title accurately reflects the primary objective of adding validation error handling and improved messaging for federation operations, matching the core changes throughout the codebase. |
| Linked Issues check | ✅ Passed | All three validation checks from FB-25 are implemented: policy vetting via isFederationDomainAllowedForUsernames, network reachability checks, and user existence validation via validateFederatedUsers, with proper error mapping. |
| Out of Scope Changes check | ✅ Passed | All changes are within scope: domain policy enforcement, user validation, error handling, and configuration for federation checks; no UI/UX or unrelated changes detected. |
✨ Finishing touches
- [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
- [ ] Commit unit tests in branch
feat/fed-errors-messaging
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.
Comment @coderabbitai help to get the list of available commands and usage tips.
📦 Docker Image Size Report
📈 Changes 
| Service | Current | Baseline | Change | Percent |
|---|---|---|---|---|
| sum of all images | 1.1GiB | 1.1GiB | +11MiB | |
| rocketchat | 355MiB | 345MiB | +11MiB | |
| omnichannel-transcript-service | 132MiB | 132MiB | +203B | |
| queue-worker-service | 132MiB | 132MiB | -402B | |
| ddp-streamer-service | 126MiB | 126MiB | +375B | |
| account-service | 113MiB | 113MiB | +857B | |
| authorization-service | 111MiB | 111MiB | +9.2KiB | |
| presence-service | 111MiB | 111MiB | +8.2KiB |
📊 Historical Trend
---
config:
theme: "dark"
xyChart:
width: 900
height: 400
---
xychart
title "Image Size Evolution by Service (Last 30 Days + This PR)"
x-axis ["11/15 22:28", "11/16 01:28", "11/17 23:50", "11/18 22:53", "11/19 23:02", "11/21 16:49", "11/24 17:34", "11/27 22:32", "11/28 19:05", "12/01 23:01", "12/02 21:57", "12/03 21:00", "12/04 18:17", "12/05 21:56", "12/08 20:15", "12/09 22:17", "12/10 23:26", "12/11 21:56", "12/12 22:45", "12/13 01:34", "12/15 22:31", "12/16 22:18", "12/17 21:04", "12/18 23:12", "12/19 23:27", "12/20 21:03", "12/22 18:54", "12/23 16:16", "12/23 19:43 (PR)"]
y-axis "Size (GB)" 0 --> 0.5
line "account-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11]
line "authorization-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11]
line "ddp-streamer-service" [0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12]
line "omnichannel-transcript-service" [0.14, 0.14, 0.14, 0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13]
line "presence-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11]
line "queue-worker-service" [0.14, 0.14, 0.14, 0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13]
line "rocketchat" [0.36, 0.36, 0.35, 0.35, 0.35, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.35]
Statistics (last 28 days):
- 📊 Average: 1.5GiB
- ⬇️ Minimum: 1.2GiB
- ⬆️ Maximum: 1.6GiB
- 🎯 Current PR: 1.1GiB
ℹ️ About this report
This report compares Docker image sizes from this build against the develop baseline.
-
Tag:
pr-37581 -
Baseline:
develop - Timestamp: 2025-12-23 19:43:00 UTC
- Historical data points: 28
Updated: Tue, 23 Dec 2025 19:43:01 GMT
Codecov Report
:white_check_mark: All modified and coverable lines are covered by tests.
:white_check_mark: Project coverage is 70.64%. Comparing base (a8a84ab) to head (decfe3d).
Additional details and impacted files
@@ Coverage Diff @@
## develop #37581 +/- ##
===========================================
- Coverage 70.66% 70.64% -0.03%
===========================================
Files 3143 3143
Lines 108694 108694
Branches 19572 19557 -15
===========================================
- Hits 76810 76783 -27
- Misses 29887 29909 +22
- Partials 1997 2002 +5
| Flag | Coverage Δ | |
|---|---|---|
| e2e | 60.18% <ø> (-0.02%) |
:arrow_down: |
| e2e-api | 47.39% <ø> (-0.08%) |
:arrow_down: |
| unit | 71.76% <ø> (-0.04%) |
:arrow_down: |
Flags with carried forward coverage won't be shown. Click here to find out more.
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
- :package: JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.