Version: 1.0.0Documentation Index
Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
Use this file to discover all available pages before exploring further.
Last Updated: 2025-01-25
Status: Active
Quick Reference: For quick lookup, see permissions-quick-reference.md
Specification: See PF-30 Specification
Table of Contents
- System Overview
- Design Decisions
- Migration History (V1 → V2)
- Review & Recommendations
- Implementation Guidelines
- References
1. System Overview
The Encore Health OS Platform uses a granular permissions system (V2) that replaced the legacy role-based system (V1). V2 provides flexible, module-specific permissions with support for custom roles, site-scoped assignments, and time-limited access.Current Status
V2 (Active - Mandatory): Granular permissions system- ✅ Flexible:
{module}.{entity}.{action}format (e.g.,hr.employees.view) - ✅ Custom roles per organization
- ✅ Site-scoped and time-limited assignments
- ✅ 165+ granular permissions
- ✅ Mandatory: All organizations use V2 exclusively (no feature flag)
pf_user_roles table
- ❌ Deprecated: No longer in use
- ❌ Table marked read-only (retained for audit/rollback purposes)
- ❌ All code migrated to V2
Permission Key Format
hr.employees.view- View HR employeesfa.bills.approve- Approve FA billsrh.residents.create- Create RH residentssystem.platform.admin- Platform admin access
Core Components
| Component | Purpose |
|---|---|
PermissionGate | Conditional rendering based on permission |
RequirePermission | Page-level protection |
useHasPermission() | Hook for permission logic |
Database Tables
| Table | Purpose |
|---|---|
pf_user_role_assignments | User-to-role assignments with org/site scope |
pf_roles | Role definitions (custom + system) |
pf_role_permissions | Permission-to-role mappings |
pf_permissions | Permission definitions |
pf_user_roles | DEPRECATED - V1 table, read-only |
2. Design Decisions
This section documents key architectural decisions for the PF-30 Permissions System V2.Decision 1: Site-Scope Interaction with Custom Roles
Chosen Approach: Custom roles are always org-scoped, site-scope is applied at assignment. Rationale:- Simplicity: Custom roles define WHAT permissions a user has. Role assignments define WHERE those permissions apply.
- Flexibility: Same custom role can be used across different sites without duplication.
- Maintainability: Changing a role’s permissions updates all assignments automatically.
- Precedent: Mirrors how system roles already work with site assignments.
- Custom roles table (
pf_custom_roles) does NOT havesite_idcolumn - Role assignments table (
pf_user_role_assignments) has optionalsite_ids UUID[]column - Permission checks must consider site scope from assignment, not from role definition
Decision 2: V2 is Mandatory (No Feature Flag)
Chosen Approach: V2 permissions system is mandatory for all organizations. Rationale:- Migration complete: All organizations have been migrated to V2
- Simplified codebase: No conditional logic based on feature flags
- Consistent behavior: All users experience the same permission system
- Reduced maintenance: Single code path to maintain
permissions_v2_enabled column still exists in the database for historical purposes but is no longer checked in code. All organizations are assumed to have V2 enabled.
Note: Feature flag has been removed post-migration. V2 is now mandatory.
Decision 3: Base Role Inheritance Constraint
Decision: Add CHECK constraint to prevent inheriting fromplatform_admin or org_admin.
Rationale:
- Security: Prevents accidental privilege escalation
- Clear hierarchy: Custom roles should not exceed org_admin permissions
- Explicit: Database-level enforcement, not just application logic
finance_adminsite_adminfinance_staffstaffreadonly
platform_admin(system-level only)org_admin(already has full org access)
Decision 4: Foreign Key References
Decision: Usepf_profiles(id) for all user references.
Rationale:
- Consistency: All other PF tables reference
pf_profiles - Query simplicity: Can join directly without crossing schema boundaries
- RLS compatibility:
pf_profileshas RLS policies,auth.usersdoesn’t - Extensibility: Additional user metadata available without joining
Decision 5: Permission Caching Strategy
Decision: Use React Query with 5-minute staleTime, database indexes for query optimization. Rationale:- Consistency with platform: Constitution §6.5 specifies 5-min staleTime, 10-min gcTime
- Balance: Fresh enough for security, cached enough for performance
- Invalidation: Role/permission changes invalidate queries immediately
3. Migration History (V1 → V2)
Migration Overview
Migration Period: December 22-23, 2025Status: ✅ COMPLETE
Total Files Modified: ~60
Migration Phases
Phase 1: Foundation ✅
- Enabled V2 for all organizations
- Updated navigation system (removed
minRole) - Updated core hooks and utilities
- Updated module registry
Phase 2: Components ✅
- Migrated dashboard components
- Migrated navigation components
- Migrated core module components
- Migrated data manager components
Phase 3: RLS & Database ✅
- Updated all V2 helper functions to use
pf_user_role_assignments - Updated component user lookups
- Updated test infrastructure with dual V1/V2 seeding
Phase 4: Testing & Cleanup ✅
- Removed deprecated V1 code (
useUserRole.ts,minRole,hasMinimumRole(),getHighestRole()) - Updated documentation (AI guides, module docs)
- Marked
pf_user_rolesas read-only
Key Changes
Code Removed:useUserRole.ts- Deleted - hook that queried V1 tableminRoleproperty - Removed from all navigation items and TypeScript typesROLE_HIERARCHY- Removed from module-registry.tsgetHighestRole()- Removed from module-registry.tshasMinimumRole()- Removed from module-registry.ts
- Helper functions query
pf_user_role_assignmentsfirst pf_user_roleshas deprecation commentpf_user_roleshas read-only trigger- V2 enabled for all organizations
Migration Patterns
Pattern 1: Navigation Items
Pattern 2: Component Permission Checks
Pattern 3: RLS Policies
Role to Permission Mapping
| V1 Role | V2 Permission Pattern |
|---|---|
platform_admin | system.platform.admin |
org_admin | system.platform.admin (or module-specific *.admin) |
site_admin | *.view + *.create + *.edit (no admin) |
staff | *.view + *.create |
readonly | *.view only |
finance_admin | fa.admin + all FA permissions |
finance_staff | fa.*.view + fa.*.create (no approve/admin) |
4. Review & Recommendations
System Architecture Review
The permissions system overhaul is architecturally sound and aligns with constitution requirements, integrates with existing systems (PF-26 object permissions), and ensures proper security, testing, and documentation. Key Findings:- ✅ Multi-tenant design correctly includes
organization_idin all tables - ✅ Uses
pf_prefix for Platform Foundation tables - ✅ Includes audit columns (
created_at,updated_at,created_by,updated_by) - ✅ Permissions system is Platform Foundation (PF), not core-specific
Database Schema Compliance
Required Schema Elements:- ✅
custom_fields JSONBon business entity tables - ✅
created_byandupdated_byaudit columns - ✅
site_idwhere applicable (for site-scoped permissions) - ✅ Descriptive comments on columns
- ✅ RLS enabled on all tables with SECURITY DEFINER functions
Integration with Existing Systems
PF-26 Object Permissions Integration: The system integrates with existingpf_object_permissions and pf_field_permissions (PF-26):
- Module Permissions (New): Control access to features/modules (e.g., “Can access HR module?”)
- Object Permissions (PF-26): Control access to specific entities (e.g., “Can view Employee records?”)
- Field Permissions (PF-26): Control field-level access (e.g., “Can edit salary field?”)
- Module permission (e.g.,
hr.employees.view) - Object permission (e.g.,
hr_employees.can_viewfor role) - Field permission (e.g.,
hr_employees.salary.permission_level)
RLS Policy Requirements
CRITICAL: All tables MUST have RLS enabled with SECURITY DEFINER functions to avoid recursion. Required RLS Policies:- Users can view org custom roles
- Org admins can manage custom roles
- All authenticated users can view permissions (system-wide, read-only)
- Users can view role permissions for their org
- Org admins can manage role permissions
- Users can view their own role assignments
- Org admins can manage role assignments
Testing Requirements
Per Constitution §3.3:| Feature Type | Unit | RLS | Integration | E2E |
|---|---|---|---|---|
| Database schema | - | ✅ Required | - | - |
| Permission checks | 80% | ✅ Required | ✅ Required | - |
| Role management UI | 80% | ✅ Required | ✅ Required | - |
| Critical flows | 80% | ✅ Required | ✅ Required | ✅ Required |
- User role assignment workflow
- Custom role creation and permission assignment
- Permission inheritance from system roles
- Cross-organization permission isolation
- ✅ No cross-organization data leakage
- ✅ Users can only view/manage roles in their organizations
- ✅ Permission checks respect tenant boundaries
- ✅ System roles have appropriate access
Security Considerations
Critical Security Requirements:- Tenant Isolation: ✅ RLS policies MUST prevent cross-org access
- Permission Escalation: ✅ Users cannot grant themselves permissions
- Role Hijacking: ✅ Custom roles cannot inherit from higher system roles
- Audit Trail: ✅ All role/permission changes logged to
pf_audit_logs
- ✅ User cannot access another org’s custom roles
- ✅ User cannot assign themselves higher permissions
- ✅ Permission checks respect site scope
- ✅ Expired role assignments are not honored
5. Implementation Guidelines
Using Permissions in Code
ALWAYS use permission service, not direct queries:Creating Custom Roles
Pattern:- Create role in
pf_custom_roles - Assign permissions via
pf_role_permissions - Assign role to users via
pf_user_role_assignments
Navigation Filtering
Anti-Patterns
- ❌ Hardcode role checks (
user.role === 'admin') - ❌ Create custom roles inheriting from
org_adminorplatform_admin - ❌ Query permission tables directly in RLS policies
- ❌ Use V1 patterns (
minRole,useUserRole(),hasMinimumRole())
6. References
Documentation
- Quick Reference - Quick lookup tables and patterns
- PF-30 Specification - Complete system specification
- Module Permissions Matrix - All available permissions
Implementation
- Permissions Component README - Component documentation
- AI Guide Permissions Patterns - AI agent patterns
- AGENTS.md Permissions Patterns - Quick reference patterns
Historical (Archived)
- Archived permissions documentation available in
docs/archive/permissions/for historical reference
Last Updated: 2025-01-25
Status: Active Documentation