Designing 44-Permission RBAC for AI Knowledge Bases
How Simmetric Chat's role-based access control uses 44 permissions and 15 menu sections to protect workspaces, projects, and audit trails.
Why AI knowledge bases need real access control
An AI workspace concentrates your most sensitive operational knowledge: internal documents, chat histories with RAG citations, agent configurations, and connectors to external systems. When that surface is open to a whole company, "who can see what" stops being a nice-to-have feature request and becomes the same class of problem as database permissions - a design decision that has to be correct by construction.
Simmetric Chat treats it that way. Every authenticated request passes through a role and permission check before it touches business data, and the same model that protects chat history also governs which parts of the application a user can even see in the sidebar. This article walks through how the system is designed: the 44 permissions that cover the platform's resource surface, the 15 menu sections that map those permissions to the user interface, and the request-level guards that stop authenticated users from reaching records that are not theirs.
The permission model: 44 granular permissions
At the core of the model is a fixed vocabulary of 44 permissions. Each permission names a resource and an action, following a resource:action convention. Workspaces, projects, chats, and documents each carry the full read/write/delete triplet, because these are the resources users create and destroy in the course of normal work:
workspace:read,workspace:write,workspace:deleteproject:read,project:write,project:deletechat:read,chat:write,chat:deletedocument:read,document:write,document:delete
Around that core sit permissions for the creation flow itself (workspace:create, project:create), for administering the platform (admin:users, admin:settings, admin:roles), and for reading or configuring LLM providers (provider:read, provider:write). Archives get the same triplet treatment as the core resources, because archived conversations are still sensitive data.
A cluster of six backup permissions separates destination management, job execution, log inspection, and restore operations - the split matters because "can trigger a backup" and "can restore production data into an arbitrary workspace" are different levels of trust. Finally, memory:read and memory:write cover the per-user, per-workspace memory system, and filters:manage gates filter administration for administrators only.
Permissions are stored as an enum table in the database and referenced by name. Custom roles are assembled by selecting from the 44-permission set, so an operator can express "archivist" or "read-only analyst" without touching code:
{
"name": "archivist",
"permissions": ["workspace:read", "document:read", "archive:read", "chat:read"]
}Two default roles ship out of the box. The admin role holds all 44 permissions; the user role holds a working subset - read access to workspaces and providers, read/write on chats and documents, read on archives, create on workspaces and projects, and full management of their own memories. Neither role is special-cased in application logic; both are ordinary rows that the permission middleware resolves like any other.
Menu sections: permissions that shape the interface
Access control that only guards API routes produces a confusing experience - users click buttons that fail. Simmetric Chat therefore also maps permissions to 15 menu sections that control sidebar visibility. The sections are the structural anatomy of the application:
dashboard, chat, documents, knowledgeBase, workspaces, projects, marketplace, mcpConnections, eventLog, analytics, widget, settings, and uploads.
Each role carries its own menu-section list, stored as strings so new sections can be added without a database migration. A user role might see dashboard, chat, documents, knowledgeBase, workspaces, widget, and uploads, while the admin role sees all 13. The result is that the interface itself reflects what the role can do: a user without analytics permissions never encounters an analytics entry point to click.
The permission checks and menu visibility are two expressions of the same underlying role, which means there is a single source of truth to reason about during an audit.
Request-level guards: RBAC as middleware
Every API request resolves the user together with roles and permissions (with a cache) and attaches the resolved identity to the request. On top of that sit four middleware guards, each answering a different question:
requirePermission- admins always pass; everyone else must hold every listed permission for the route. Unauthenticated requests get401; authenticated users without sufficient permissions get403with an explicit "Insufficient permissions" error.requireAdmin- hard gate for administrative routes, answering403"Admin access required" for non-admins.requireProjectAccess- an IDOR check against project ownership and explicit access rows.requireWorkspaceAccess- the same class of check for workspaces, accepting workspace ownership, parent-project ownership, or an explicit access grant.
The last two deserve emphasis because they address the vulnerability class that plagues multi-tenant systems. An IDOR (insecure direct object reference) attack is what happens when /api/workspaces/123 trusts the identifier in the URL more than the identity of the caller. A user who is authenticated - but not authorized for workspace 123 - must not be able to read its documents by guessing identifiers. Simmetric Chat answers those requests with 401 when the user is unauthenticated and 403 "Insufficient permissions" when the user simply lacks access, rather than leaking the existence or contents of the resource.
Route protection is declarative. Workspace CRUD requires requireWorkspaceAccess; creating a workspace requires the workspace:create permission and honors plan limits; role administration lives behind requireAdmin. The route table reads like the security policy, which is exactly the property you want when a new engineer asks "what can this endpoint do?"
Audit trails: knowing who did what
Permission checks answer "should this action be allowed"; audit trails answer "what actually happened." Simmetric Chat records an audit trail of significant platform actions - authentication events, document operations, settings changes, and administrative actions - in a browsable event log.
The combination is what makes RBAC operational rather than theoretical. When an unexpected document deletion occurs, the event log identifies the actor and the action; the permission model explains which role allowed it; and adjusting either a role's permissions or its menu sections closes the gap. The audit surface is itself permission-gated through the eventLog menu section, so the record of security-relevant activity is visible only to roles that were trusted with it.
Sensitive retention changes - such as configuring chat message retention days - are themselves audited, so even the controls that govern data lifecycle leave their own trace.
What this looks like in practice
Consider a three-role deployment. Administrators keep the full 44-permission set and all 15 menu sections. A daily-user role works within chats, documents, and their own workspaces, and never sees the event log or provider settings. A compliance auditor gets read-only permissions on documents, chats, and archives, plus access to the event log - able to inspect anything, change nothing.
None of this required forking the application or writing custom middleware. It required assembling permissions into roles and mapping menu sections. That is the design goal the 44-permission model serves: fine-grained enough to express real organizational boundaries, simple enough that the policy fits in one's head.
To see the full permission vocabulary and the role management endpoints, read the API reference. For a hands-on tour of roles, permissions, and menu sections in the admin interface, see the usage guide.
