Moderation System Architecture
Moderation System Architecture
Overview
The moderation system provides automated content moderation for user-generated content including chat messages and reviews. It uses a multi-layered approach combining OpenAIās Moderation API, custom LLM analysis, and Googleās Perspective API to ensure content safety while minimizing false positives.
System Flow
flowchart TD A[Message Created] --> B[InstantDB Subscriber] B --> C{Already Moderated?} C -->|Yes| Z[Skip] C -->|No| D[Add to Moderation Queue] D --> E[ChatModerationProcessor] E --> F[ModerationService.runModeration] F --> G[OpenAI Moderation API] G --> H{Flagged?}
H -->|No| I[Enqueue Notification] I --> J[Mark as Moderated]
H -->|Yes| K[LLM Flagging Analysis] K --> L[Determine Action] L --> M{Room Type?} M -->|Category Room| N[Perspective API Scores] M -->|Group/Private| O[LLM Sentiment Analysis] N --> P[Update Message with Results] O --> P P --> Q[Skip Notification]Components
1. InstantDB Subscriber Service
Location: server/src/instantdb/subscriber.service.ts
Subscribes to real-time message updates from InstantDB and queues unmoderated messages for processing.
| Responsibility | Description |
|---|---|
| Subscription Management | Creates and maintains WebSocket subscriptions to InstantDB |
| Idempotency | Skips already-moderated messages |
| Job Deduplication | Uses messageId as BullMQ jobId to prevent duplicates |
| Health Monitoring | Cron job checks subscription health every minute |
2. Moderation Service
Location: server/src/moderation/moderation.service.ts
Orchestrates the moderation pipeline and determines final actions.
async runModeration(type: ModerationType, data: any): Promise<ModerationResultDto>Moderation Types:
CHAT- Chat messagesREVIEW- Product/service reviews
3. Analysis Service
Location: server/src/moderation/analysis.service.ts
Provides AI/ML analysis capabilities:
| Method | Provider | Purpose |
|---|---|---|
checkOpenAIModeration() | OpenAI | Binary flag detection (fast, first-pass) |
getFlaggingDetailsFromLLM() | OpenRouter | Detailed severity analysis for flagged content |
getSentimentFromLLM() | OpenRouter | Sentiment scoring (-1 to 1) |
getPerspectiveScores() | Google Perspective API | Toxicity, profanity, and sexual content scores |
4. Queue Processors
Location: server/src/moderation/processors/
| Processor | Queue | Job Type |
|---|---|---|
ChatModerationProcessor | chat-moderation | MODERATE_CHAT |
ReviewModerationProcessor | review-moderation | MODERATE_REVIEW |
CommentModerationProcessor | comment-moderation | MODERATE_COMMENT |
Decision Flow
Step 1: OpenAI Moderation (Gate)
All content first passes through OpenAIās Moderation API. This is a fast, binary check.
- Not Flagged: Immediately enqueue notification, skip detailed analysis
- Flagged: Proceed to detailed LLM analysis
Step 2: LLM Flagging Analysis
For flagged content, the LLM provides:
reason: Human-readable explanationrecommendedAction:RETAIN,WARN, orREMOVEneedsHumanReview: Boolean for edge cases
Step 3: Context-Specific Analysis
Additional analysis based on content type and context:
| Context | Analysis |
|---|---|
| Category Room Chat | Perspective API (toxicity, profanity, sexual) |
| Group/Private Chat | LLM Sentiment Analysis |
| Reviews | LLM Sentiment + Perspective API |
Data Model
ModerationResultDto
{ messageId?: string; reviewId?: string; isFlagged: boolean; action: ModerationAction; needsHumanReview: boolean; moderationReason?: string; sentimentScore?: number; sentimentLabel?: SentimentLabel; toxicityScore?: number; profanityScore?: number; sexualScore?: number; moderatedAt: Date;}ModerationAction Enum
RETAIN- Content is safeWARN- Content is borderline, may need reviewREMOVE- Content should be hidden/removed
SentimentLabel Enum
POSITIVENEUTRALNEGATIVE
Configuration
Environment variables:
| Variable | Description |
|---|---|
OPENAI_API_KEY | OpenAI API key for moderation |
OPENROUTER_API_KEY | OpenRouter API key for LLM |
OPENROUTER_BASE_URL | OpenRouter base URL |
PERSPECTIVE_API_KEY | Google Perspective API key |
MODERATION_LLM_MODEL | Model ID for LLM analysis |
MODERATION_SENTIMENT_PROMPT | Prompt template for sentiment |
MODERATION_FLAGGING_PROMPT | Prompt template for flagging |
Moderation Flow Implementation & Cleanup
Implemented a robust, sequential moderation-to-notification flow and performed comprehensive code cleanup.
Changes Made
1. ModerationāNotification Flow Restructure
- Sequential Processing: Modified
ModerationServiceto trigger notifications only after OpenAI moderation passes. - Notification Gate: OpenAI moderation acts as the first filter. If content is flagged, detailed LLM analysis runs but notifications are skipped.
- Decoupled Subscriber: Removed notification logic from
instantdb.service.tsandsubscriber.service.ts, making them purely responsible for queuing moderation. - Idempotency: Leveraged BullMQ
jobIdfor robust deduplication.
2. Code Cleanup & Optimization
- Debug Logs: Removed all
this.logger.debugstatements from moderation-related services. - Console Logs: Removed
console.logstatements fromanalysis.service.ts. - Comment Stripping: Removed all JSDoc and internal comments from:
moderation.service.tsanalysis.service.tsinstantdb.service.ts
- Protocol Fix: Verified and restored URLs corrupted during comment stripping.
Verification Results
Integration Flow
sequenceDiagram participant IDB as InstantDB participant SUB as Subscriber participant MOD as ModerationService participant NOTIF as NotificationService
IDB->>SUB: New Message (moderated: false) SUB->>MOD: Add Moderation Job MOD->>MOD: OpenAI Moderation Check alt Content is Clean MOD->>NOTIF: Enqueue Notification Job MOD->>IDB: Mark as Moderated (isFlagged: false) else Content is Flagged MOD->>MOD: LLM Severity Analysis MOD->>IDB: Mark as Moderated (isFlagged: true) Note over MOD: Skips Notification endClean Code Verification
Ran grep checks confirming 0 instances of debug logs and comments in the modified service files.
Queue Configuration
Moderation queues are configured with:
removeOnComplete: false(for debugging/audit)attempts: 3with exponential backoffjobId: chat-mod-{messageId}for deduplication
Integration Points
Inbound
- InstantDB Subscriptions: Real-time message stream
- Direct API Calls: For batch moderation
Outbound
- Notification Service: Enqueues notifications for clean content
- InstantDB Updates: Marks messages as moderated with results
Notification Flow
Notifications are only sent for content that passes OpenAI moderation:
- OpenAI check returns
!flagged enqueueNotificationForChat()fetches message details- Validates sender/room metadata
- Queues notification job
- Marks message as
notificationProcessedAt
Flagged content skips notification entirely.