generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } enum SubscriptionTier { BASIC PLUS PREMIUM } enum IdentifierType { EMAIL PHONE SSN } enum WatchListStatus { ACTIVE PAUSED } enum Severity { INFO WARNING CRITICAL } enum AlertChannel { EMAIL PUSH SMS } enum AlertStatus { PENDING SENT READ } enum ScanJobStatus { PENDING RUNNING COMPLETED FAILED } enum DataSource { HIBP SECURITY_TRAILS CENSYS SHODAN HONEYPOT } enum AnalysisJobStatus { PENDING RUNNING COMPLETED FAILED } enum AnalysisType { SYNTHETIC_DETECTION VOICE_MATCH BATCH } enum DetectionVerdict { NATURAL SYNTHETIC UNCERTAIN } model User { id String @id @default(uuid()) email String @unique name String? subscriptionTier SubscriptionTier @default(BASIC) familyGroupId String? watchListItems WatchListItem[] alerts Alert[] scanJobs ScanJob[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([email]) } model WatchListItem { id String @id @default(uuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) identifierType IdentifierType identifierValue String identifierHash String @unique status WatchListStatus @default(ACTIVE) exposures Exposure[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([userId]) @@index([identifierHash]) } model Exposure { id String @id @default(uuid()) watchListItemId String watchListItem WatchListItem @relation(fields: [watchListItemId], references: [id], onDelete: Cascade) dataSource DataSource breachName String exposedAt DateTime dataType String[] severity Severity details String? contentHash String @unique alert Alert? createdAt DateTime @default(now()) @@index([watchListItemId]) @@index([contentHash]) @@index([dataSource]) } model Alert { id String @id @default(uuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) exposureId String @unique exposure Exposure @relation(fields: [exposureId], references: [id], onDelete: Cascade) severity Severity channel AlertChannel status AlertStatus @default(PENDING) dedupKey String sentAt DateTime? createdAt DateTime @default(now()) @@index([userId, status]) @@index([dedupKey]) } model ScanJob { id String @id @default(uuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) status ScanJobStatus @default(PENDING) source DataSource? resultCount Int @default(0) errorMessage String? completedAt DateTime? createdAt DateTime @default(now()) @@index([userId, status]) @@index([createdAt]) } model VoiceEnrollment { id String @id @default(uuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) label String embeddingVector Float[] embeddingDim Int @default(192) audioFilePath String? sampleRate Int @default(16000) durationSec Float? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([userId]) @@index([embeddingDim]) } model AnalysisJob { id String @id @default(uuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) analysisType AnalysisType audioFilePath String status AnalysisJobStatus @default(PENDING) result AnalysisResult? errorMessage String? completedAt DateTime? createdAt DateTime @default(now()) @@index([userId, status]) @@index([createdAt]) } model AnalysisResult { id String @id @default(uuid()) analysisJobId String @unique analysisJob AnalysisJob @relation(fields: [analysisJobId], references: [id], onDelete: Cascade) syntheticScore Float verdict DetectionVerdict matchedEnrollmentId String? matchedSimilarity Float? confidence Float processingTimeMs Int modelVersion String? metadata String? createdAt DateTime @default(now()) @@index([analysisJobId]) @@index([verdict]) }