Skip to main content

Data model

34 models, 12 enums, at apps/web/prisma/schema.prisma (673 lines).

:::note The old README said 19 models It listed 19 and placed the schema in packages/db. There are 34, and packages/db is empty. :::

Authentication

ModelPurpose
UserCore identity
AccountOAuth links (NextAuth)
SessionActive sessions
VerificationTokenEmail verification
OtpOne-time codes — legacy, flow not wired up

Social

ModelPurpose
FriendshipUser connections
ChatDM conversations
MessagesDM contents
TestimonialLanding page quotes

Groups

ModelPurpose
GroupTeam, optional githubRepo
GroupMemberMembership, role, codeAccess
GroupMessageGroup chat
InviteEmail invitations
GroupInviteLinkTokenised join links
GuestUser, GuestGroupLimited guest access
enum GroupRole { OWNER ADMIN MEMBER }

enum CodeAccessStatus {
NONE // no repository access
PENDING_GITHUB // awaiting collaborator invite
INVITED // invite sent, unaccepted
ACTIVE // full access
}

codeAccess is the gate on repository writes. Only ACTIVE may commit.

Files & version control

ModelPurpose
FileTracked repository files
ModifiedFilesWorking-tree changes
ChangeIndividual edits
ChangeRequestReview unit
ApprovedCr, RejectedCrReview outcomes
enum ChangeRequestStatus { OPEN MERGED REJECTED CONFLICT }
enum FileStatus { /* tracked file lifecycle */ }

CONFLICT is set when the base SHA moved between raising and merging a CR.

Workspace

ModelPurpose
WorkspaceBoardJSON { nodes, edges } for graph boards
PlanningColumnKanban columns
PlanningTaskTasks
PlanningTaskDependencyTask edges — must stay acyclic
PlanningAssigneeTask assignment
PlanningMilestoneTimeline milestones
enum WorkspaceBoardType { MIND_MAP PLANNING DB_SCHEMA UI_DESIGN }
enum PlanningPriority { /* task priority */ }

Two storage strategies live side by side: the three graph boards store a JSON document on WorkspaceBoard, while planning uses real relational tables. The graph boards need whole-document replacement anyway; planning needs per-task queries and assignment, which JSON would make painful.

Calls

ModelPurpose
CallRoomA call: room name, type, status, initiator
CallParticipantPer-user: joined/left, muted, video, screen sharing
CallRecordingSchema only — not implemented
PushSubscriptionWeb Push endpoints
enum CallStatus { RINGING ONGOING ENDED MISSED REJECTED }
enum CallType { /* 1:1 or group */ }
enum RecordingStatus { /* unused */ }

Other

ModelPurpose
NotificationsIn-app feed
BugReportReports that become GitHub issues
enum BugStatus { … }
enum BugSeverity { … }
enum BugCategory { … }

Relationships

User ─┬─ GroupMember ─── Group ─┬─ GroupMessage
│ ├─ WorkspaceBoard
│ ├─ PlanningTask ─── PlanningTaskDependency
│ ├─ ChangeRequest ─┬─ ApprovedCr
│ │ └─ RejectedCr
│ └─ CallRoom ────── CallParticipant
├─ Account / Session
├─ Friendship
├─ Chat ─── Messages
└─ Notifications

Group is the hub. Most queries start from a groupId and a membership check — which is exactly why authorization bugs in this codebase tend to be missing membership checks rather than broken ones.

Working with the schema

cd apps/web
npx prisma studio # browse
npx prisma migrate dev --name <name> # new migration
npx prisma generate # regenerate the client
npx prisma validate # syntax check
npx prisma migrate status # drift check

Always regenerate after a schema edit, or TypeScript will not see new fields. Review generated SQL before committing — Prisma will write a destructive migration if the change implies one.

Dead schema

Model / enumWhy it is here
OtpOTP sign-in, no longer wired up
CallRecording, RecordingStatusRecording specced, never built

Neither is harmful, but both mislead. See Known limitations.