This document covers the multi-user management system in AnythingLLM, including role-based access control (RBAC), user lifecycle management, invitation system, and workspace-level permissions. It details how the system supports both single-user and multi-user deployment modes, and how administrators and managers control user access.
For authentication mechanisms (JWT, password hashing, session management), see Authentication System. For workspace-level configuration and settings inheritance, see Workspace Configuration.
AnythingLLM operates in one of two modes, controlled by the multi_user_mode setting in the system_settings database table.
In single-user mode, the instance is protected by an optional instance password (AUTH_TOKEN environment variable). All requests are authenticated with this single credential, and there is no concept of individual users or roles.
Sources: server/endpoints/system.js296-329 server/endpoints/system.js647-655
Multi-user mode enables:
Enabling Multi-User Mode
The transition from single-user to multi-user mode is one-way and irreversible:
Sources: server/endpoints/system.js591-644 frontend/src/models/system.js204-215
The enabling process performs the following operations:
SystemSettings to set multi_user_mode: trueJWT_SECRET for token signingBrowserExtensionApiKey.migrateApiKeysToMultiUser()Sources: server/endpoints/system.js619-633
The users table tracks all user accounts in multi-user mode:
| Field | Type | Description |
|---|---|---|
id | INTEGER | Primary key |
username | TEXT | Unique username for login |
password | TEXT | bcrypt-hashed password |
role | TEXT | Role: 'admin', 'manager', 'default' |
suspended | INTEGER | 0 = active, 1 = suspended |
pfpFilename | TEXT | Profile picture filename (nullable) |
seen_recovery_codes | BOOLEAN | Whether user has viewed recovery codes |
createdAt | DATETIME | Account creation timestamp |
lastUpdatedAt | DATETIME | Last modification timestamp |
Sources: Based on usage patterns in server/models/user.js
Sources: server/utils/middleware/multiUserProtected.js server/endpoints/admin.js36-46
The system defines roles in server/utils/middleware/multiUserProtected.js:
Sources: server/utils/middleware/multiUserProtected.js39
Role Validation Rules
The validRoleSelection() function enforces these constraints:
Sources: server/endpoints/admin.js49-82 server/utils/helpers/admin.js
User updates follow similar validation patterns with additional protections:
Last Admin Protection
The canModifyAdmin() function prevents:
Sources: server/endpoints/admin.js85-123 server/utils/helpers/admin.js
User deletion is permanent and cascades to related records:
Cascade Effects:
workspace_users table)Sources: server/endpoints/admin.js126-155
Suspended users (suspended = 1) cannot:
Suspended status is checked at:
Sources: server/endpoints/system.js238-254 server/endpoints/system.js118-127
The invitation system enables admins and managers to create registration links for new users.
Sources: server/models/invite.js server/endpoints/admin.js158-221
Sources: server/endpoints/admin.js172-201 server/models/invite.js
| Status | Description |
|---|---|
pending | Unused invite, can be claimed |
accepted | Used by a user, cannot be reused |
disabled | Deactivated by admin, cannot be claimed |
Sources: server/models/invite.js
Workspace access is controlled via the workspace_users join table.
Sources: server/models/workspaceUsers.js server/models/workspace.js
Add Users to Workspace
The /admin/workspaces/:workspaceSlug/manage-users endpoint supports two modes:
Additive mode (reset: false, default):
Reset mode (reset: true):
Sources: server/endpoints/api/admin/index.js547-659
| Scenario | Access Granted? |
|---|---|
Admin user, no workspace_users entry | ✓ Yes (admins bypass check) |
Manager user, no workspace_users entry | ✗ No |
Default user, has workspace_users entry | ✓ Yes |
Default user, no workspace_users entry | ✗ No |
Suspended user, has workspace_users entry | ✗ No (suspension overrides all) |
Sources: server/utils/middleware/multiUserProtected.js
The multiUserProtected.js file provides role-based middleware:
Middleware Types:
strictMultiUserRoleValid([roles])
flexUserRoleValid([roles])
isMultiUserSetup
Sources: server/utils/middleware/multiUserProtected.js server/endpoints/admin.js35-47
| Endpoint | Middleware | Required Roles |
|---|---|---|
POST /admin/users/new | strictMultiUserRoleValid | admin, manager |
POST /system/update-env | flexUserRoleValid | admin |
GET /admin/workspaces | strictMultiUserRoleValid | admin, manager |
DELETE /system/remove-document | flexUserRoleValid | admin, manager |
GET /system/pfp/:id | flexUserRoleValid | all (any authenticated user) |
Sources: server/endpoints/system.js server/endpoints/admin.js
The AuthContext manages authentication state in the React frontend:
Token Refresh Mechanism:
On mount and whenever store.authToken changes, the AuthContext calls System.refreshUser():
If the user is suspended or session is invalid, the context clears all auth data and redirects to login.
Sources: frontend/src/AuthContext.jsx50-72 server/endpoints/system.js143-180
Users can upload and manage their profile picture (pfp):
Profile Picture Endpoints:
POST /system/upload-pfp (roles: all)GET /system/pfp/:id (roles: all, user can only fetch own pfp)DELETE /system/remove-pfp (roles: all)Sources: server/endpoints/system.js764-801 server/endpoints/system.js855-888
| Method | Endpoint | Roles | Description |
|---|---|---|---|
| GET | /admin/users | admin, manager | List all users |
| POST | /admin/users/new | admin, manager | Create new user |
| POST | /admin/user/:id | admin, manager | Update user by id |
| DELETE | /admin/user/:id | admin, manager | Delete user by id |
API Version:
| Method | Endpoint | Roles | Description |
|---|---|---|---|
| GET | /v1/admin/users | API key | List all users |
| POST | /v1/admin/users/new | API key | Create new user |
| POST | /v1/admin/users/:id | API key | Update user by id |
| DELETE | /v1/admin/users/:id | API key | Delete user by id |
Sources: server/endpoints/admin.js35-155 server/endpoints/api/admin/index.js41-267
| Method | Endpoint | Roles | Description |
|---|---|---|---|
| GET | /admin/invites | admin, manager | List all invites |
| POST | /admin/invite/new | admin, manager | Create new invite |
| DELETE | /admin/invite/:id | admin, manager | Deactivate invite |
API Version:
| Method | Endpoint | Roles | Description |
|---|---|---|---|
| GET | /v1/admin/invites | API key | List all invites |
| POST | /v1/admin/invite/new | API key | Create new invite |
| DELETE | /v1/admin/invite/:id | API key | Deactivate invite |
Sources: server/endpoints/admin.js158-221 server/endpoints/api/admin/index.js270-422
| Method | Endpoint | Roles | Description |
|---|---|---|---|
| GET | /admin/workspaces/:workspaceId/users | admin, manager | List users with workspace access |
| POST | /admin/workspaces/:workspaceId/update-users | admin, manager | Deprecated: Overwrite workspace users |
| POST | /v1/admin/workspaces/:workspaceSlug/manage-users | API key | Add/reset workspace users |
manage-users Request Body:
Sources: server/endpoints/admin.js238-288 server/endpoints/api/admin/index.js425-659
| Method | Endpoint | Roles | Description |
|---|---|---|---|
| POST | /system/enable-multi-user | none (pre-auth) | Enable multi-user mode |
| GET | /system/multi-user-mode | none | Check if multi-user mode enabled |
| POST | /system/recover-account | none | Recover account with recovery codes |
| POST | /system/reset-password | none | Reset password with token |
| GET | /system/refresh-user | validated token | Refresh user object from session |
Sources: server/endpoints/system.js591-655
bcrypt with salt rounds = 10users.password columnSources: server/models/user.js
Upon first login, users receive single-use recovery codes for account recovery:
Recovery codes are:
POST /system/recover-accountSources: server/endpoints/system.js277-287 server/utils/PasswordRecovery/index.js
JWT tokens contain:
id: User IDusername: UsernameJWT_EXPIRY env var (default: "30d")The userFromSession() helper extracts and validates the user on each request:
Sources: server/utils/http.js
Suspended users are blocked at multiple layers:
AuthContext logs out suspended usersSources: server/endpoints/system.js238-254 frontend/src/AuthContext.jsx50-72
The settings sidebar dynamically shows options based on user role:
Each Option component checks user.role against allowed roles array before rendering.
Sources: frontend/src/components/SettingsSidebar/index.jsx214-419
Components use patterns like:
The Option component filters out options where user.role is not in roles array.
Sources: frontend/src/components/SettingsSidebar/MenuOption.jsx
POST /admin/users/newusers tableuser_created eventSources: server/endpoints/admin.js49-82
POST /admin/invite/newinvites record and workspace_invite entries/invite/:code and registersSources: server/endpoints/admin.js172-201
POST /admin/workspaces/:id/update-usersworkspace_users entriesSources: server/endpoints/admin.js272-288
Sources: server/endpoints/system.js server/endpoints/admin.js server/models/user.js server/models/invite.js server/models/workspaceUsers.js server/utils/middleware/multiUserProtected.js frontend/src/models/system.js frontend/src/AuthContext.jsx frontend/src/components/SettingsSidebar/index.jsx
Refresh this wiki