Banning prevents a user from participating in a specific room. Unlike kicking (which deletes the subscription), banning keeps the subscription record with status: 'BANNED', creating a persistent access barrier.
- A user with
ban-userpermission (roles:admin,owner,moderator) triggers the ban via UI, API (POST /v1/rooms.banUser), or slash command (/ban @username). - Validations (
banUserFromRoomMethodinserver/lib/banUserFromRoom.ts):- Checks
ban-userpermission scoped to the room. - Checks if the room type allows the action (via
roomDirectives.allowMemberAction). - Checks if the banning user has access to the room.
- Checks if the target user exists and is in the room.
- Rejects ban if the target is already banned.
- Rejects ban if the target is the last owner.
- Checks
- Execution (
performUserBaninapp/lib/server/functions/banUserFromRoom.ts):- Updates the subscription to
status: 'BANNED'(does not delete the record). - Removes the room from the user's
__roomsarray. - Decrements the room's
usersCount. - Removes room-scoped roles (
moderator,owner,leader) in channels and groups. - If the room is a team's main room, removes the member from the team.
- Saves a
user-bannedsystem message. - Notifies the client with a
removedevent on the subscription (so the client drops the stream).
- Updates the subscription to
- Callback
afterBanFromRoomfires (used by Matrix federation to propagate the ban).
- Triggered via UI (contextual bar "Banned Users"), API (
POST /v1/rooms.unbanUser), or slash command (/unban @username). - Finds the subscription via
findOneBannedSubscription. - Removes the subscription entirely (
Subscriptions.removeById) — does not restore it to active status. - Saves a
user-unbannedsystem message. - Callback
afterUnbanFromRoomfires (federation).
Important: after unban the user does not become a member of the room again. The banned subscription is deleted. The user must be invited or join again.
A banned user cannot re-enter the room through any path. The ban must be explicitly lifted first. Below is how each entry point enforces this for both normal and federated rooms.
addUsersToRoom checks for a BANNED subscription before calling addUserToRoom:
- Returns
error-user-is-banned— the invite is rejected. - The UI shows a warning modal asking the admin to unban first.
- Applies equally to normal and federated rooms (the check is in the method layer, before the room-type branch).
useInviteToken checks for a BANNED subscription before saving the invite token or calling addUserToRoom:
- Returns
error-user-is-banned— the token is not consumed. - Because the check runs before
Users.updateInviteToken, the secondary path throughsetUsername(for users who register via invite link) is also blocked.
Room.join calls canAccessRoom before addUserToRoom:
- For public rooms and public rooms inside teams, the
canAccessRoomvalidators explicitly checkfindOneBannedSubscriptionand deny access. - For private rooms,
countByRoomIdAndUserIdexcludesBANNEDsubscriptions (status: { $exists: false }), so the "already joined" validator returns false and access is denied.
When a Matrix homeserver sends an invite for a user who is banned locally:
handleInviteinfederation-matrix/src/events/member.tsfinds the existing (banned) subscription and returns early without creating a new one.- The user never receives an
INVITEDsubscription, sohandleJoinis never reached.
- Unban the user via
POST /v1/rooms.unbanUser,/unban @username, or the "Banned Users" contextual bar. This deletes the banned subscription. - Invite or join — the user can now be invited (API, UI, invite link) or join (public rooms) normally.
The canAccessRoom validators check for bans in two public room scenarios:
- Public rooms inside teams — if banned, access is denied.
- Regular public rooms — if banned, access is denied.
For private rooms, access is controlled by the subscription: countByRoomIdAndUserId excludes BANNED subscriptions, so a banned user has no valid subscription and cannot access the room.
- Ban action: appears in the user info panel (inside a room), gated by
ban-userpermission +roomCanBan+ federation rules. - Banned users list: "Banned Users" tab in the room toolbox (icon:
ban, order: 13, requiresban-user), with virtualized scroll and infinite pagination viaGET /v1/rooms.bannedUsers. - Unban action: context menu on each item in the banned users list.
- Confirmation: both actions show a
GenericModalwithdangervariant.
| Key | When |
|---|---|
user-banned |
A user is banned from the room |
user-unbanned |
A user is unbanned (including via re-addition) |
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/rooms.banUser |
Ban a user (accepts userId or username + roomId) |
| POST | /v1/rooms.unbanUser |
Unban a user |
| GET | /v1/rooms.bannedUsers |
List banned users (paginated) |
| Layer | File |
|---|---|
| API routes | app/api/server/v1/rooms.ts |
| Validation & permissions | server/lib/banUserFromRoom.ts |
| Core ban logic | app/lib/server/functions/banUserFromRoom.ts |
| Core unban logic | app/lib/server/functions/executeUnbanUserFromRoom.ts |
| Slash commands | app/slashcommands-ban/server/ban.ts, unban.ts |
| Client ban hook | client/views/room/hooks/useBanUser.tsx |
| Client unban hook | client/views/room/hooks/useUnbanUser.tsx |
| Ban action (user info) | client/views/room/hooks/useUserInfoActions/actions/useBanUserAction.tsx |
| Banned users UI | client/views/room/contextualBar/BannedUsers/ |
| Subscription types | packages/core-typings/src/ISubscription.ts |
| REST typings | packages/rest-typings/src/v1/rooms.ts |
| Model typings | packages/model-typings/src/models/ISubscriptionsModel.ts |