Overview
Every user has a global rolethat determines platform-wide privileges. Within each project or program, a user's effective role is computed by combining their global role, their relationship to the resource (e.g. creator), and their explicit membership.
Global Roles
Global roles are assigned at the platform level and apply across all projects and programs.
| Role | Description | Effective Project Role |
|---|---|---|
SUPER_ADMIN | Full platform administrator with unrestricted access | → ADMIN in all projects |
ADMIN | Platform administrator | → ADMIN in all projects |
FELLOW | Recognized research fellow with elevated trust | → MAINTAINER in all projects |
MEMBER | Standard registered user (default) | → Based on membership |
Project Roles
A user's effective project role is determined by the following priority:
- Global
SUPER_ADMINorADMIN→ ADMIN - Global
FELLOW→ MAINTAINER - Project creator → OWNER
- Explicit project membership → assigned role
- No membership → VIEWER (read-only)
| Role | Settings | Members | Wiki Edit | Effort Create | Forum Post | Delete Project |
|---|---|---|---|---|---|---|
| ADMIN | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| OWNER | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| MAINTAINER | ✅ | View only | ✅ | ✅ | ✅ | ❌ |
| CONTRIBUTOR | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ |
| VIEWER | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
Project Action Matrix
Detailed permissions for each action within a project:
| Action | Required Role |
|---|---|
| View project | Anyone (public) |
| Access settings page | MAINTAINER+ |
| Update project details | MAINTAINER+ |
| Manage members (add/remove/role change) | ADMIN or OWNER |
| Archive project | MAINTAINER+ |
| Delete project | ADMIN or OWNER |
| Restore deleted project | ADMIN or OWNER |
| Create wiki page | Authenticated user |
| Edit wiki page | CONTRIBUTOR+ |
| Delete wiki page | MAINTAINER+ |
| Submit wiki for review | CONTRIBUTOR+ |
| Review/approve wiki version | MAINTAINER+ |
| Create forum thread | Authenticated user |
| Delete forum thread | MAINTAINER+ or author |
| Pin/lock/unlock thread | MAINTAINER+ |
| Move thread to another project | ADMIN or OWNER |
| Create forum post | Authenticated user |
| Edit/delete forum post | MAINTAINER+ or author |
| Create workspace effort | Authenticated user |
| Update workspace effort | CONTRIBUTOR+ |
| Delete workspace effort | MAINTAINER+ or author |
| Manage effort types | MAINTAINER+ |
Program Roles
Programs use a separate role hierarchy. The effective role is determined by:
- Global
SUPER_ADMIN/ADMIN→ full access - Global
FELLOW→ curator-level access - Program creator → creator (highest program role)
- Explicit program membership → assigned role
- No membership → read-only
| Role | Settings | Members | Link Projects | Wiki Edit | Delete Program |
|---|---|---|---|---|---|
| creator | ✅ | ✅ | ✅ | ✅ | ✅ |
| curator | ✅ | ✅ | ✅ | ✅ | ❌ |
| contributor | ❌ | ❌ | ❌ | ✅ | ❌ |
| viewer | ❌ | ❌ | ❌ | ❌ | ❌ |
Program Action Matrix
| Action | Required Role |
|---|---|
| View program | Anyone (public) |
| Access settings page | curator+ or global ADMIN/FELLOW |
| Update program details | curator+ |
| Manage members | creator or global ADMIN |
| Link/unlink projects | curator+ |
| Create program wiki page | contributor+ |
| Edit program wiki page | contributor+ (not viewer) |
| Delete program | creator or global ADMIN |
| Delete program + all descendants | creator or global ADMIN |
Backend Enforcement
All write operations are enforced server-side using tRPC middleware:
- protectedProcedure — Requires authentication. All mutations use this middleware, which rejects unauthenticated requests with a
401 UNAUTHORIZEDerror and enforces rate limiting (60 mutations/min per user). - Role checks— After authentication, each mutation verifies the user's role using
getUserProjectRole()for projects or program membership queries. Insufficient permissions return a403 FORBIDDENerror.
Frontend Guards
The frontend enforces permissions at two levels:
- Page-level guards — Settings pages check authentication and role before rendering. Unauthenticated users see a sign-in prompt; insufficient roles see an access denied message.
- Action-level guards — Buttons and forms (edit, delete, create) are conditionally rendered based on
isAuthenticatedand role checks. For example, the delete button only appears for ADMIN/OWNER roles.
Permission Helper Functions
The codebase provides utility functions in src/lib/permissions.ts:
| Function | Returns true for |
|---|---|
getUserProjectRole() | Computes effective ProjectRole from global role, creator status, and membership |
canDeleteProject(role) | ADMIN, OWNER |
canRestoreProject(role) | ADMIN, OWNER |
canManageMembers(role) | ADMIN, OWNER |
isMaintainerOrAbove(role) | ADMIN, OWNER, MAINTAINER |
isContributorOrAbove(role) | ADMIN, OWNER, MAINTAINER, CONTRIBUTOR |
canDeleteThread(role, isAuthor) | ADMIN, OWNER, MAINTAINER, or author |
canDeleteWorkspaceEffort(role, isAuthor) | ADMIN, OWNER, MAINTAINER, or author |