Appearance
Roles & Permissions
Complete reference for DoCurious user roles, admin sub-roles, vendor team roles, and community roles.
Role Architecture Overview
DoCurious uses a multi-context role model where each user has:
- One primary role (
AppUser.role) — determines account type and base permissions - Optional admin sub-role (
AppUser.adminSubRole) — granular feature-area access for platform_admin/staff - Optional vendor team membership (
VendorTeamMember) — per-vendor role for shared vendor accounts - Optional community membership (
CommunityMember) — per-community role for moderation hierarchy
Users also hold contexts simultaneously (personal + school + business + platform). See the Architecture guide for the context model.
Primary Roles (9)
Every user has exactly one primary role stored in AppUser.role.
| Role | Display Name | Description | Context |
|---|---|---|---|
user | User | General user — personal challenge doer | Personal only |
student | Student | Student within a school | Personal + School |
parent | Parent | Parent/guardian linked to child accounts | Personal (parent is a relationship) |
teacher | Teacher | Teacher within a school | Personal + School |
school_admin | School Admin | School administrator | Personal + School |
head_school_admin | Head School Admin | Head SA — can create other school admins | Personal + School |
vendor | Vendor | Marketplace vendor who creates paid challenges | Personal + Business |
staff | Staff | DoCurious internal staff (verification, support, etc.) | Personal + Platform |
platform_admin | Platform Admin | Full platform administrator | Personal + Platform |
Key Distinction
School admin ≠ Platform admin. School admins manage their school (roster, classes, assignments). Platform admins manage the DoCurious platform (users, content, config). They are completely separate contexts.
Role Hierarchy
platform_admin (full platform access)
└── staff (feature-area access via adminSubRole)
head_school_admin (can create school_admin accounts)
└── school_admin (manages school)
└── teacher (manages classes, assigns challenges)
└── student (completes challenges)
└── parent (views child activity, grants consent)
vendor (manages vendor business)
└── vendor team members (owner > manager > contributor)
user (personal challenge doer — base role)Student Account Tiers (COPPA)
Students have an additional accountTier field:
| Tier | Description | Restrictions |
|---|---|---|
tier_1_school_only | School acts as COPPA agent | No gifting, no saved list, limited social |
tier_2_full | School + parent linked | Full platform features |
standard | Users 13+ | Unrestricted access |
Admin Sub-Roles (7)
When role is platform_admin or staff, the adminSubRole field controls which feature areas they can access.
| Sub-Role | Display Name | Feature Areas |
|---|---|---|
super | Super Admin | All 10 feature areas |
content | Content Admin | challenge_management, verification, content_moderation |
school | School Admin (Platform) | school_management, user_management |
vendor | Vendor Admin (Platform) | vendor_management, user_management |
support | Support | user_management, content_moderation |
analytics | Analytics | analytics |
engineering | Engineering | system_config, analytics, audit_log, feature_flags |
Feature Areas (10)
| Feature Area | Description |
|---|---|
user_management | View/edit/suspend user accounts |
challenge_management | Approve/reject/edit challenges |
verification | Review and verify track records |
content_moderation | Handle flagged content and reports |
school_management | Manage school accounts and DPAs |
vendor_management | Manage vendor accounts and payouts |
analytics | View platform analytics dashboards |
system_config | Manage system configuration |
audit_log | View audit trail |
feature_flags | Toggle feature flags |
Permission Matrix
| Sub-Role | Users | Challenges | Verify | Moderate | Schools | Vendors | Analytics | Config | Audit | Flags |
|---|---|---|---|---|---|---|---|---|---|---|
| super | x | x | x | x | x | x | x | x | x | x |
| content | x | x | x | |||||||
| school | x | x | ||||||||
| vendor | x | x | ||||||||
| support | x | x | ||||||||
| analytics | x | |||||||||
| engineering | x | x | x | x |
Vendor Team Roles (3)
Vendors can have multiple team members with different access levels, stored in the VendorTeamMember table.
| Role | Description | Permissions |
|---|---|---|
owner | Business owner | Full access: challenge CRUD, orders, analytics, team management, payouts |
manager | Operations manager | Challenge CRUD, order management, analytics (no payout access) |
contributor | Content contributor | Create/edit challenges only (no orders, analytics, or team management) |
Database Model
prisma
model VendorTeamMember {
id String @id @default(uuid())
userId String
vendorId String
role String @default("contributor") // owner, manager, contributor
invitedBy String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([userId, vendorId])
}Community Roles (4)
Each community has its own role hierarchy, stored in the CommunityMember table.
| Role | Description | Permissions |
|---|---|---|
owner | Community creator | Full control: settings, roles, delete community |
admin | Community administrator | Manage members, edit settings, moderate content |
moderator | Community moderator | Moderate posts, mute/warn members, manage flags |
member | Regular member | Post, comment, react, participate |
Membership Statuses
| Status | Description |
|---|---|
active | Full member with role-based access |
pending | Join request awaiting approval |
invited | Invitation sent but not yet accepted |
banned | Removed from community |
Database Model
prisma
model CommunityMember {
id String @id @default(uuid())
userId String
communityId String
role String @default("member") // owner, admin, moderator, member
status String @default("active") // active, pending, invited, banned
joinedAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([userId, communityId])
}Multi-Context Model
Users can hold multiple contexts simultaneously. The primary role maps to contexts via buildContextsFromLegacyRole() in contextHelpers.ts.
| Context | When Active | Sub-Roles |
|---|---|---|
personal | Always (every user) | — |
school | When user has a school role | student, teacher, school_admin, head_school_admin |
business | When user is a vendor | owner, manager, contributor |
platform | When user is staff or platform_admin | staff, admin |
Context Switching
The UI's context switcher (header dropdown) allows users to switch between their active contexts. Each context shows different navigation, dashboards, and feature sets.
Route Guards
The frontend enforces role-based access through layered route guards:
| Guard | Purpose | Example |
|---|---|---|
AuthGuard | Requires authentication | All protected routes |
ContextGuard | Requires specific context + optional role | ContextGuard({ context: 'school', roles: ['teacher', 'school_admin'] }) |
AdminRoleGuard | Requires admin sub-role with specific feature area | AdminRoleGuard({ features: ['user_management'] }) |
TierGuard | Requires minimum account tier | TierGuard({ minTier: 'tier_2_full' }) |
AgeGuard | Requires minimum age | AgeGuard({ minAge: 13 }) |
ConsentGuard | Requires parental consent | COPPA-protected features |
ParentGuard | Requires parent relationship | Parent dashboard routes |
Backend Implementation
Prisma Schema
The role system lives in three tables:
app_users—role(String) +adminSubRole(String, nullable)vendor_team_members—userId+vendorId+rolecommunity_members—userId+communityId+role+status
Middleware
| Middleware | File | Purpose |
|---|---|---|
requireRole(...roles) | server/src/middleware/requireRole.ts | Check primary role |
requireAdminFeature(...features) | server/src/middleware/requireRole.ts | Check admin sub-role has feature area access |
requireAuth | server/src/middleware/auth.ts | Verify JWT, attach req.user |
JWT Token Payload
typescript
interface TokenPayload {
userId: string
role: string // Primary role
adminSubRole?: string // Admin sub-role (when applicable)
}The adminSubRole is included in both access and refresh tokens so that requireAdminFeature() middleware can check permissions without a database query.
FE Type Definitions
| Type | File | Values |
|---|---|---|
UserRole | src/types/user.types.ts | 9 primary roles |
AdminRole | src/types/admin.types.ts | 7 admin sub-roles |
ADMIN_PERMISSIONS | src/types/admin.types.ts | Sub-role → feature area mapping |
SchoolRole | src/types/context.types.ts | student, teacher, school_admin, head_school_admin |
BusinessRole | src/types/context.types.ts | owner, manager, contributor |
PlatformRole | src/types/context.types.ts | staff, admin |
CommunityRole | src/types/community.types.ts | owner, admin, moderator, member |
MembershipStatus | src/types/community.types.ts | active, pending, invited, banned |