Add cross-service alert correlation system FRE-4500

- Unified alert types (AlertSource, AlertCategory, CorrelationStatus, EntityType)
- NormalizedAlert and CorrelationGroup Prisma models
- AlertNormalizer for all 4 services (DarkWatch, SpamShield, VoicePrint, CallAnalysis)
- CorrelationEngine with temporal + entity-based correlation detection
- CorrelationService orchestrator with dashboard API
- Correlation API routes (/api/v1/correlation/*)
- Service emitters wired to DarkWatch, SpamShield, VoicePrint
- pnpm workspace config for monorepo
This commit is contained in:
Senior Engineer
2026-05-02 01:10:44 -04:00
committed by Michael Freno
parent 685fb57e53
commit 03276dde2d
35 changed files with 8072 additions and 31 deletions

View File

@@ -25,8 +25,11 @@ enum WatchListStatus {
}
enum Severity {
LOW
INFO
MEDIUM
WARNING
HIGH
CRITICAL
}
@@ -88,10 +91,12 @@ model User {
scanSchedules ScanSchedule[]
voiceEnrollments VoiceEnrollment[]
analysisJobs AnalysisJob[]
spamFeedback SpamFeedback[]
spamCallAnalyses SpamCallAnalysis[]
spamAuditLogs SpamAuditLog[]
createdAt DateTime @default(now())
spamFeedback SpamFeedback[]
spamCallAnalyses SpamCallAnalysis[]
spamAuditLogs SpamAuditLog[]
normalizedAlerts NormalizedAlert[]
correlationGroups CorrelationGroup[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([email])
@@ -332,3 +337,76 @@ model SpamAuditLog {
@@index([createdAt])
@@index([decision])
}
enum AlertSource {
DARKWATCH
SPAMSHIELD
VOICEPRINT
CALL_ANALYSIS
}
enum AlertCategory {
BREACH_EXPOSURE
SPAM_CALL
SPAM_SMS
SYNTHETIC_VOICE
VOICE_MISMATCH
CALL_QUALITY
CALL_ANOMALY
CALL_EVENT
}
enum CorrelationStatus {
ACTIVE
RESOLVED
FALSE_POSITIVE
}
enum EntityType {
PHONE_NUMBER
EMAIL
USER_ID
CALL_ID
IP_ADDRESS
}
model NormalizedAlert {
id String @id @default(uuid())
source AlertSource
category AlertCategory
severity Severity
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
title String
description String
entities Json // [{ type: EntityType, value: string }]
sourceAlertId String
groupId String?
correlationGroup CorrelationGroup? @relation(fields: [groupId], references: [id], onDelete: SetNull)
payload Json
createdAt DateTime @default(now())
@@index([userId, createdAt])
@@index([groupId])
@@index([sourceAlertId])
@@index([source])
@@index([severity])
}
model CorrelationGroup {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade, map: "corr_user_idx")
entities Json // [{ type: EntityType, value: string }]
highestSeverity Severity
status CorrelationStatus @default(ACTIVE)
alertCount Int @default(0)
alerts NormalizedAlert[]
summary String?
resolvedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId, status])
@@index([createdAt])
}