Documentation Index
Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
Use this file to discover all available pages before exploring further.
Version: 1.0.0
Last Updated: 2026-01-11
Status: Active
Overview
This document defines the standard Row Level Security (RLS) patterns used across Encore Health OS. All business tables MUST have RLS enabled with appropriate policies.Core Principles
1. SECURITY DEFINER Helper Functions
Never query RLS-protected tables directly in policies - this causes infinite recursion.2. WITH CHECK on UPDATE Policies
Always include WITH CHECK clause to prevent tenant data movement.3. Multi-Tenant Isolation
All policies MUST filter byorganization_id (and site_id where applicable).
4. Defense in Depth
RLS is one layer - also include application-level checks in mutations.Standard Patterns
Pattern A: Full CRUD (Business Entities)
For tables where users with org access can perform all operations.Pattern B: Admin-Managed (Settings/Config)
For tables where only admins can write but all org users can read.Pattern C: Audit/Log (Append-Only)
For tables that should never be modified after creation.pf_audit_logs,pf_index_cleanup_audithr_document_access_logs,hr_payroll_audit_log,hr_ssn_access_logfw_approval_history,fw_domain_events,fw_signature_audit_logfm_asset_maintenance_history,fm_work_order_history
Pattern D: User-Owned (Personal Data)
For tables where users own their own records.pf_profiles,pf_object_favorites,pf_notification_batcheshr_employee_preferences
Pattern E: Version/History (Immutable)
For tables storing historical versions - SELECT + INSERT only.pf_document_versions,fw_form_versions,fw_workflow_versionsgr_policy_versions,lo_knowledge_article_versions
Pattern F: Service-Only (System Tables)
For tables only accessible via service_role key.pf_integration_credentials
Helper Functions by Module
| Module | Has Org Access | Is Org Admin | Is Module Admin |
|---|---|---|---|
| PF | pf_has_org_access(org_id, user_id) | pf_is_org_admin(user_id, org_id) | pf_is_platform_admin(user_id) |
| HR | hr_has_org_access(org_id, user_id) | hr_is_org_admin(org_id, user_id) | - |
| FA | fa_has_org_access(org_id, user_id) | fa_is_org_admin(org_id, user_id) | fa_is_finance_admin(user_id, org_id) |
| CE | ce_has_org_access(org_id, user_id) | ce_is_org_admin(org_id, user_id) | Permission-based admin via ce.%.admin grant |
| FW | fw_has_org_access(org_id, user_id) | fw_is_org_admin(org_id, user_id) | - |
| FM | fm_has_org_access(org_id, user_id) | fm_is_org_admin(org_id, user_id) | - |
| GR | gr_has_org_access(org_id, user_id) | gr_is_org_admin(org_id, user_id) | gr_is_compliance_officer(...) |
| RH | rh_has_org_access(org_id, user_id) | - | - |
| LO | lo_has_org_access(org_id, user_id) | - | - |
ce_is_org_admin is SECURITY DEFINER and returns true when the caller is an org admin or has any granted permission key matching ce.%.admin. Use this permission-based admin check for CE workflows that delegate admin responsibilities via module permissions. Use role-only *_is_org_admin checks when delegated module-admin permissions are not part of the access model.
Note: Parameter order can vary by helper (for example pf_is_org_admin(user_id, org_id) vs most module helpers (...org_id, ...user_id)) - always check function signature.
Verification Queries
Check Tables Without RLS
Check Policy Coverage
Check for Recursion Risks
Check UPDATE Policies Without WITH CHECK
Migration Checklist
When adding RLS to a new table:- Enable RLS:
ALTER TABLE {table} ENABLE ROW LEVEL SECURITY; - Add SELECT policy with org access check
- Add INSERT policy with org access check
- Add UPDATE policy with BOTH
USINGANDWITH CHECK - Add DELETE policy (usually admin-only)
- Use SECURITY DEFINER helper functions (never direct queries)
- Add RLS tests in
tests/rls/
See Also
- Database Development Guide
- RLS Policy Audit
- Constitution §5.7 - RLS Requirements