docs/features/ban-user.md
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.
ban-user permission (roles: admin, owner, moderator) triggers the ban via UI, API (POST /v1/rooms.banUser), or slash command (/ban @username).banUserFromRoomMethod in server/lib/banUserFromRoom.ts):
ban-user permission scoped to the room.roomDirectives.allowMemberAction).performUserBan in app/lib/server/functions/banUserFromRoom.ts):
status: 'BANNED' (does not delete the record).__rooms array.usersCount.moderator, owner, leader) in channels and groups.user-banned system message.removed event on the subscription (so the client drops the stream).afterBanFromRoom fires (used by Matrix federation to propagate the ban).POST /v1/rooms.unbanUser), or slash command (/unban @username).findOneBannedSubscription.Subscriptions.removeById) — does not restore it to active status.user-unbanned system message.afterUnbanFromRoom fires (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.
groups.invite, channels.invite, "Add Users")addUsersToRoom checks for a BANNED subscription before calling addUserToRoom:
error-user-is-banned — the invite is rejected.useInviteToken)useInviteToken checks for a BANNED subscription before saving the invite token or calling addUserToRoom:
error-user-is-banned — the token is not consumed.Users.updateInviteToken, the secondary path through setUsername (for users who register via invite link) is also blocked.channels.join, joinRoom)Room.join calls canAccessRoom before addUserToRoom:
canAccessRoom validators explicitly check findOneBannedSubscription and deny access.countByRoomIdAndUserId excludes BANNED subscriptions (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:
handleInvite in federation-matrix/src/events/member.ts finds the existing (banned) subscription and returns early without creating a new one.INVITED subscription, so handleJoin is never reached.POST /v1/rooms.unbanUser, /unban @username, or the "Banned Users" contextual bar. This deletes the banned subscription.The canAccessRoom validators check for bans in two public room scenarios:
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-user permission + roomCanBan + federation rules.ban, order: 13, requires ban-user), with virtualized scroll and infinite pagination via GET /v1/rooms.bannedUsers.GenericModal with danger variant.| 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 |