Files
bitvid/docs/moderationService-overview.md

5.0 KiB

Moderation Service Overview

File: js/services/moderationService.js

Purpose

The ModerationService implements the decentralized Trust & Safety model for bitvid. It filters content based on a "Web of Trust" derived from the current user's social graph (NIP-02 Contact List, Kind 3) and administrative overrides.

Instead of a central authority deciding what is "safe," the service aggregates reports (NIP-56) only from users the viewer follows (their "trusted contacts"). This creates a personalized moderation layer where spam and abuse are filtered by community consensus within the user's network.

Key Concepts

Web of Trust

  • Viewer Context: The service tracks the currently logged-in user (viewerPubkey).
  • Trusted Contacts: The set of pubkeys the viewer follows (from their Kind 3 event).
  • Trust Score: The number of unique trusted users who have reported a specific piece of content.
  • Thresholds: Actions (e.g., blurring, disabling autoplay) are triggered when the Trust Score exceeds defined limits (AUTOPLAY_TRUST_THRESHOLD, BLUR_TRUST_THRESHOLD).

NIP-56 Reports (Kind 1984)

  • Users can report content for reasons like nudity, spam, illegal, etc.
  • Reports are public events signed by the reporter.
  • The service subscribes to reports for active content and aggregates them locally.
  • Crucially: Reports from users outside the trusted contact list are ignored. Creator whitelist membership does not establish moderation trust.

NIP-51 Mutes (Kind 10000)

  • Users can mute other users to hide their content entirely.
  • The service syncs with UserBlocks to enforce mutes.
  • It also supports "Trusted Mutes" where mutes from trusted contacts can influence visibility (though this feature is evolving).

Admin Overrides

  • The service integrates with AccessControl to respect an instance-wide Blacklist (always hidden). The creator whitelist is access-only and never establishes moderation trust.

Architecture & Data Flow

1. Initialization (Setting the Viewer)

When a user logs in, setViewerPubkey(pubkey) is called:

  1. The service fetches the user's latest Kind 3 Contact List.
  2. It extracts all followed pubkeys into trustedContacts.
  3. It merges any admin-defined "Trust Seeds" (default trusted users).
  4. It recomputes trust scores for all currently loaded content based on the new graph.

2. Report Aggregation

When viewing a video, the app calls subscribeToReports(eventId):

  1. The service subscribes to Kind 1984 events referencing the video ID.
  2. Incoming reports are ingested via ingestReportEvent.
  3. recomputeSummaryForEvent checks if the reporter is in trustedContacts.
  4. If trusted, the report increments the score for its category.
  5. If the score crosses a threshold, the service emits a summary event, updating the UI (e.g., to blur the video).

Usage Example

import moderationService from './services/moderationService.js';

// 1. Initialize with the logged-in user
await moderationService.setViewerPubkey(myPubkey);

// 2. Subscribe to reports for a video
await moderationService.subscribeToReports(videoEventId);

// 3. Listen for trust score updates
const unsubscribe = moderationService.on('summary', ({ eventId, summary }) => {
  if (summary.totalTrusted >= 1) {
    console.log(`Video ${eventId} is flagged by trusted users!`);
    // UI logic: blur thumbnail, show warning
  }
});

// 4. Submit a report (as the viewer)
await moderationService.submitReport({
  eventId: videoEventId,
  type: 'nudity',
  targetPubkey: videoAuthorPubkey,
  content: 'Contains explicit content'
});

Public API Summary

Method Description
setViewerPubkey(pubkey) Sets the active user and rebuilds the trust graph from their contacts.
subscribeToReports(eventId) Starts listening for NIP-56 reports for the given event ID.
submitReport(params) Publishes a signed Kind 1984 report event to relays.
getTrustedReportSummary(eventId) Returns the current aggregated trust score and report types.
isPubkeyBlockedByViewer(pubkey) Checks if the author is blocked/muted by the viewer.
refreshViewerFromClient() Helper to sync the viewer with the active Nostr client state.

Invariants

  • Trust is Subjective: A report only counts if the viewer follows the reporter. Changing the viewer changes all trust scores immediately.
  • Admin Trumps Trust: Blacklisted users are ignored even if followed. Creator whitelist membership neither grants nor removes trust.
  • Reactive: The service emits events (summary, contacts, user-blocks) to drive UI updates; it does not directly manipulate the DOM.

When to Change

  • Refactoring: Logic for "Trusted Mutes" (NIP-51 lists from others) is complex and partially deprecated/evolved. This area may need cleanup.
  • Performance: If the number of reports or contacts grows large, the in-memory aggregation (reportEvents map) might need optimization or worker offloading.
  • Protocol Updates: Changes to NIP-56 or NIP-51 should be reflected here.