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:
| Role | Can read | Can write | Tenancy |
|---|---|---|---|
| admin | All institutions (servicer-operations view) | Yes | institutionId = null (cross-institution) |
| reviewer | Their own institution's cases | Yes, within their institution | Scoped to one institutionId |
| auditor | Read-only | No (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:
- Requires a valid signed session (else 401).
- Blocks auditors from mutating (
auditorrole → 403, read-only). - Optionally enforces admin-only routes.
- Requires a same-origin request (origin/referer must match).
- Requires a valid CSRF token (double-submit cookie + header, bound to the user id).
- 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
profilesrow (docs/pilot-setup.md). - Demo users: shareable demo identities (admin / reviewer / auditor) for evaluation, with no external credentials.
- Sessions: httpOnly, SameSite=Lax,
securein 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.