Agency RBAC Persistence Model
Purpose
This document describes the org RBAC persistence shape that is actually in the repo today.
Use it to understand where role, capability, and client-scope state currently lives.
Current Ownership Boundary
organizationsandorgUsersown base org membershiporg/accessowns capability resolution and role-policy loadingclientAccessAssignmentsowns non-owner client visibility in the client-work slice
Implemented Persistence Layers
1. Base membership layer
Current table:
orgUsers
Current fields used by the org slice:
organizationIduserIdemailroleasowner,admin,account_manager, orstaffstatuscapabilityModecapabilityOverrides
This is already the source of truth for the base org role.
2. Client access scope layer
Current table:
clientAccessAssignments
Current fields:
organizationIdmemberUserIdclientAccountIdactiveassignedByUserIdassignedAtupdatedAt
Current behavior:
- owners bypass this table and get global client visibility
- non-owners derive client visibility from active assignment rows
- there is no separately persisted non-owner
all_clientsaccess mode in the current slice
3. Role policy layer
Current table:
orgRolePolicies
Current fields:
organizationIdadminPermissionsaccountManagerPermissionsstaffPermissionsupdatedByUserIdupdatedAt
This is already used by the current org capability system.
4. Per-membership capability overrides
Current storage location:
orgUsers.capabilityModeorgUsers.capabilityOverrides
Current behavior:
role_defaultuses the organization policy defaultscustomuses the explicit override list on the membership
The repo does not currently use a separate orgPermissionOverrides table.
Current Resolution Order
The current org resolution flow is:
- resolve active
orgUsersmembership - if role is
owner, grant global client visibility - load org role policy from
orgRolePolicies - apply membership
capabilityModeandcapabilityOverrides - for non-owners, resolve visible client accounts through active
clientAccessAssignments - apply route-level checks such as billing capability requirements
Important Current Constraint
The client-work slice now uses capability-first enforcement for operational authorization paths.
- capability checks gate org settings, billing, and client-work mutation/read authorization paths
- role checks remain for ownership invariants and role-transition rules (for example owner transfer and owner-protection rules)
The app still does not expose a separate client-work permission grammar beyond the existing capability set and client-scope model.
Backend Placement Rule
Keep the current ownership split intact while finishing the phase:
- membership resolution in
organizations/* - capability resolution in
org/access/* - client visibility in
org/clientScope.ts
Do not introduce a second client-scope persistence model while the current one is already working.