← Trust centerWorking document · gaps declared inline

Role-Based Access Control Model

Last reviewed: 2026-06-26. Status: current.


Roles

Eli has three roles, defined in lib/session-token.ts (Role type) and enforced server-side in lib/security.ts:

RoleCan readCan writeTenancy
adminAll institutions (servicer-operations view)YesinstitutionId = null (cross-institution)
reviewerTheir own institution's casesYes, within their institutionScoped to one institutionId
auditorRead-onlyNo (mutations blocked)Scoped or cross-institution

The session payload carries userId, email, role, institutionId, and name, signed with HMAC-SHA256 (lib/session-token.ts).


How access is enforced

Enforcement is application-layer, in front of the database. The browser never queries Supabase directly; Row-Level Security is on for every table and only the server's service-role key can read or write (supabase/migrations/0005_enable_rls.sql). This is a deliberate design: tenancy and role logic live in lib/security.ts and the repo layer, where they are unit-testable and visible, rather than spread across SQL policies.

Every staff mutation passes requireStaffMutation() (lib/security.ts), which:

  1. Requires a valid signed session (else 401).
  2. Blocks auditors from mutating (auditor role → 403, read-only).
  3. Optionally enforces admin-only routes.
  4. Requires a same-origin request (origin/referer must match).
  5. Requires a valid CSRF token (double-submit cookie + header, bound to the user id).
  6. Applies a rate limit (90 mutations/user/minute; fails open if the limiter is down).

Read access is tenant-scoped: a scoped reviewer requesting another institution's case URL gets a 404, and the copilot's tools filter results by the user's institutionId (app/api/copilot/route.ts).


Segregation of duties (second-reviewer)

An institution can require that a different reviewer sign off before any correction is written to the federal system. The preparer cannot also be the releaser; this is enforced in lib/server-actions.ts (supervisorSignOffFps()), where a correction held in pending_supervisor state requires a second identity to release it. This supports the maker/checker control a Title IV program review expects on federal writes.


Authentication

  • Pilot users: Supabase Auth (email + password), mapped to a role and institution via a profiles row (docs/pilot-setup.md).
  • Demo users: shareable demo identities (admin / reviewer / auditor) for evaluation, with no external credentials.
  • Sessions: httpOnly, SameSite=Lax, secure in production; 12-hour absolute TTL with a sliding window, so a captured token cannot outlive the window.
  • MFA: roadmap (see ../inputs/decisions.md §6).

Access review

Pilot onboarding/offboarding is a documented manual procedure today (create/disable the Supabase auth user and profiles row). A periodic access-review cadence must be established per pilot. Review actions and agent runs are captured in the tamper-evident workflow trail; general record reads, authentication/security events, and every mutation are not yet covered by a dedicated access log.

Source of truth: trust-packet/02-security/rbac-model.md in the repo.

Next: Human sign-off matrix →