Skip to main content

Agency RBAC Persistence Model

Ownership boundaries

  • orgUsers owns organization membership, role, member capability mode, and client-access mode.
  • orgRolePolicies owns organization-wide default capabilities for non-owner roles.
  • clientAccessAssignments owns the selected-client set for memberships in Selected clients mode.
  • org/access/* owns team management, invitations, hierarchy checks, and capability resolution.
  • clients/scope.ts resolves client visibility for client-scoped application data.

Do not introduce a second client-scope persistence model.

Membership state

orgUsers stores:

  • organizationId and userId
  • email, role, and status
  • clientAccessMode as all or assigned
  • capabilityMode as role default or custom
  • optional capabilityOverrides

The product label for the persisted assigned value is Selected clients. Owners resolve as All clients regardless of stored legacy values.

Selected-client rows

clientAccessAssignments stores:

  • organizationId
  • memberUserId
  • clientAccountId
  • active
  • assignment actor and timestamps

Rows are validated against the active organization. Updating a selected set activates, deactivates, or inserts only the necessary rows. Switching to All clients deactivates active selected-client rows so stale selections cannot unexpectedly reappear as effective access.

Provisional memberships created for pending invitations may also have selected-client rows. Revoking an unaccepted invitation removes those rows. An expired invitation is never active access, and an attempted expired-token consumption also removes the provisional state.

Resolution order

For an authenticated organization request:

  1. Resolve the active orgUsers membership.
  2. Resolve the role policy and any supported per-member capability override.
  3. Resolve client scope: owners and all memberships have global client access; assigned memberships use active assignment rows.
  4. Require both the operation capability and client visibility at the backend boundary.
  5. Apply ownership and hierarchy invariants for team-management operations.

Role checks are reserved for invariants such as ownership and management hierarchy. Operational authorization should use capabilities plus client scope.

Write rules

  • Team access writes are centralized in org/access/*; client pages do not mutate assignment rows.
  • Selected client IDs must belong to the active organization before any synchronization occurs.
  • No-op updates should not patch membership or assignment rows.
  • A role change returns the member to role-default capabilities unless an explicit privileged flow says otherwise.
  • Non-owners may not self-manage, manage peers or superiors, promote to their rank or above, or grant capabilities they do not possess.
  • Invitation creation must reject an email that is already an active organization member.
  • Membership, invite, role, and access-scope changes must remain auditable.

Migration compatibility

The storage value remains assigned; V1 changes its user-facing meaning to Selected clients without requiring a data migration. Existing all and assigned memberships therefore keep their effective behavior.