Permissions Registry
Overview
The Permissions Registry is a build-time generated file that provides O(1) runtime lookups for all permissions and roles. It consolidates permissions from multiple sources into pre-computed data structures.
Location: core/lib/registries/permissions-registry.ts
Generated by: core/scripts/build/registry/generators/permissions-registry.mjs
Purpose
The Permissions Registry:
- Merges permissions from core, teams, features, and entities
- Pre-computes role matrices for O(1) permission checks
- Provides type-safe exports for runtime usage
- Eliminates runtime computation by doing all work at build time
Data Sources
The registry merges data from these sources (in order of precedence):
| Source | Location | Description |
|---|---|---|
| Core System | core/lib/permissions/system.ts |
8 base system permissions |
| Theme Teams | permissions.config.ts → teams |
Team-level permissions |
| Theme Features | permissions.config.ts → features |
Feature permissions |
| Theme Entities | permissions.config.ts → entities |
Entity CRUD permissions |
| Custom Roles | permissions.config.ts → roles |
Additional roles beyond core |
Generated Exports
Permissions Data
// All merged permissions
export const ALL_RESOLVED_PERMISSIONS: ResolvedPermission[]
// All permission IDs
export const ALL_PERMISSIONS: Permission[]
// Set for O(1) existence check
export const ALL_PERMISSIONS_SET: Set<Permission>
Role Data
// Permissions indexed by role (Set for O(1) lookup)
export const PERMISSIONS_BY_ROLE: Record<string, Set<Permission>>
// Also as arrays for iteration
export const ROLE_PERMISSIONS_ARRAY: Record<string, Permission[]>
// Team permissions by role
export const TEAM_PERMISSIONS_BY_ROLE: Record<string, string[]>
// Raw team permissions (for UI)
export const TEAM_PERMISSIONS_RAW: PermissionAction[]
Custom Roles
// Custom roles configuration
export const CUSTOM_ROLES: RolesConfig
// All available roles (core + custom)
export const AVAILABLE_ROLES: readonly string[]
// Role hierarchy values
export const ROLE_HIERARCHY: Record<string, number>
// Role display names (translation keys)
export const ROLE_DISPLAY_NAMES: Record<string, string>
// Role descriptions
export const ROLE_DESCRIPTIONS: Record<string, string>
UI Data
// Permissions indexed by category
export const PERMISSIONS_BY_CATEGORY: Record<string, ResolvedPermission[]>
// Full matrix for Admin Panel UI
export const FULL_MATRIX: Record<Permission, Record<string, boolean>>
// UI sections for grouping
export const UI_SECTIONS: PermissionUISection[]
Metadata
export const PERMISSIONS_METADATA = {
totalPermissions: number,
corePermissions: number,
teamPermissions: number,
featurePermissions: number,
entityPermissions: number,
customRoles: number,
availableRoles: number,
categories: number,
generatedAt: string,
theme: string,
}
Runtime Usage
Using PermissionService (Recommended)
import { PermissionService } from '@/core/lib/services/permission.service'
// Unified API for all permission types
PermissionService.canDoAction('admin', 'team.edit') // O(1)
PermissionService.canDoAction('admin', 'customers.create') // O(1)
PermissionService.canDoAction('editor', 'page-builder.access') // O(1)
// Owner has ALL permissions
PermissionService.canDoAction('owner', 'anything') // Always true
// Get role permissions
const perms = PermissionService.getRolePermissions('admin')
Direct Registry Access
import {
PERMISSIONS_BY_ROLE,
AVAILABLE_ROLES,
ROLE_HIERARCHY,
FULL_MATRIX,
} from '@/core/lib/registries/permissions-registry'
// Direct O(1) lookup
const hasPermission = PERMISSIONS_BY_ROLE['admin']?.has('customers.create')
// Get all available roles
console.log(AVAILABLE_ROLES) // ['owner', 'admin', 'member', 'viewer', 'editor']
// Check role hierarchy
const adminLevel = ROLE_HIERARCHY['admin'] // 50
const editorLevel = ROLE_HIERARCHY['editor'] // 5
// Render permissions matrix in UI
Object.entries(FULL_MATRIX).forEach(([permission, roleMap]) => {
console.log(permission, roleMap)
// 'customers.create' { owner: true, admin: true, member: false, viewer: false }
})
Build Process
The registry is regenerated when you run:
node core/scripts/build/registry.mjs
What Gets Generated
// Auto-generated header
/**
* Auto-generated Permissions Registry
*
* Generated at: 2025-12-29T15:36:49.817Z
* Theme: default
* Team permissions: 11
* Feature permissions: 7
* Entity permissions: 23
*
* This file provides pre-computed permissions data for O(1) runtime lookups.
* All permission matrices are computed at build time.
*
* DO NOT EDIT - This file is auto-generated by scripts/build-registry.mjs
*/
Generator Logic
The generator (permissions-registry.mjs):
- Reads
permissions.config.tsfrom active theme - Reads
system.tsfor core permissions - Merges all permissions with proper precedence
- Computes role matrices
- Generates TypeScript file with all exports
Architecture
permissions.config.ts (Theme)
│
├── roles: { additionalRoles, hierarchy, ... }
├── teams: [{ action, roles, ... }]
├── features: [{ action, roles, ... }]
└── entities: { entity: [{ action, roles, ... }] }
│
▼
permissions-registry.mjs (Generator)
│
▼
permissions-registry.ts (Generated)
│
├── ALL_RESOLVED_PERMISSIONS
├── PERMISSIONS_BY_ROLE (Set)
├── TEAM_PERMISSIONS_BY_ROLE
├── CUSTOM_ROLES
├── AVAILABLE_ROLES
├── ROLE_HIERARCHY
└── FULL_MATRIX
│
▼
PermissionService (Runtime API)
│
├── canDoAction(role, action)
├── hasPermission(role, permission)
└── getRolePermissions(role)
Performance
| Operation | Complexity | Notes |
|---|---|---|
| Permission check | O(1) | Set.has lookup |
| Get role permissions | O(1) | Pre-computed array |
| Check if permission exists | O(1) | Set.has lookup |
| Get full matrix | O(1) | Pre-computed object |
| Module load | Instant | No runtime computation |
Integration with config-sync.ts
The registry is consumed by config-sync.ts for unified configuration:
// core/lib/config/config-sync.ts
import {
AVAILABLE_ROLES as REGISTRY_AVAILABLE_ROLES,
ROLE_HIERARCHY as REGISTRY_ROLE_HIERARCHY,
ROLE_DISPLAY_NAMES as REGISTRY_ROLE_DISPLAY_NAMES,
ROLE_DESCRIPTIONS as REGISTRY_ROLE_DESCRIPTIONS,
} from '@/core/lib/registries/permissions-registry'
// Team roles come from permissions-registry (single source of truth)
mergedConfig.teamRoles = {
availableTeamRoles: REGISTRY_AVAILABLE_ROLES,
hierarchy: REGISTRY_ROLE_HIERARCHY,
displayNames: REGISTRY_ROLE_DISPLAY_NAMES,
descriptions: REGISTRY_ROLE_DESCRIPTIONS,
}
Troubleshooting
Registry Not Updated
Problem: Changes to permissions.config.ts not reflected
Solution:
node core/scripts/build/registry.mjs
TypeScript Errors After Changes
Problem: Type errors after modifying permissions
Solution:
- Regenerate registry
- Restart TypeScript server
- Check for circular imports
Permission Not Found
Problem: canDoAction returns false unexpectedly
Solution:
- Verify permission is defined in
permissions.config.ts - Check the role is included in the permission's
rolesarray - Run registry regeneration
Best Practices
- Never edit the generated file - It's overwritten on each build
- Always regenerate after config changes - Run
node core/scripts/build/registry.mjs - Use PermissionService - Don't access registry directly unless necessary
- Trust pre-computed data - All lookups are O(1), no need for caching
- Check metadata - Use
PERMISSIONS_METADATAto verify generation