Built-in roles
Each tenant has three built-in roles. Permissions are additive — Owner has all permissions.
Built-in role names (
Owner, Admin, Member) are reserved. You cannot create a custom role with those names.
How permissions are enforced on the backend
Controllers use the[HasPermission] attribute:
HasPermission attribute is backed by a dynamic ASP.NET Core authorization policy provider. There is no startup registration of policies. The provider creates a policy for any permission string on demand:
Custom roles
Any tenant member with thetenant.roles.manage permission can create custom roles. Custom roles have a configurable subset of the ten available permissions.
- Role name must be unique within the tenant
- Role name cannot be
Owner,Admin, orMember(reserved) - At least one permission must be selected
- Permission strings must be valid (unknown strings are rejected)
Permission propagation timing
RBAC in the frontend
usePermissions() hook — returns an object of { hasPermission: (permission: string) => boolean }. Reads from the decoded JWT in the Zustand auth store.
PermissionGate component — wraps content that should only render for users with a specific permission:
requiredPermission. Commands the user does not have permission for are not shown. See Command Palette for details.