diff --git a/content/community-guidelines.md b/content/community-guidelines.md index 31d273dc..6a68447c 100644 --- a/content/community-guidelines.md +++ b/content/community-guidelines.md @@ -56,7 +56,7 @@ To maintain a functional and ethical platform, the following types of content ar bitvid enforces these guidelines through client-side visibility rules that match the current `moreMenuController` and `userBlocks` behavior: -- **Blurred previews:** When at least three F1 contacts file trusted NIP-56 `nudity` reports, thumbnails render blurred and autoplay previews are paused. Every blur includes a “show anyway” override. +- **Blurred previews:** When trusted NIP-56 `nudity` reports reach the configured blur threshold (`DEFAULT_BLUR_THRESHOLD` in [`config/instance-config.js`](../config/instance-config.js); upstream default: three trusted reporters), thumbnails render blurred and autoplay previews pause. Every blur includes a “show anyway” override. - **Downranked authors:** Muting a creator adds them to your NIP-51 mute list (kind `10000`). Videos from muted creators are deprioritized but still visible with clear badges so you can reverse the action. - **Personal blocks:** Blocking a creator adds them to your private block list (`userBlocks`). Videos from blocked creators are hidden from feeds, carousels, and modals unless you explicitly unblock them. - **Admin hard hides:** Moderator-maintained blacklists (NIP-51 kind `30000`) remove creators from default feeds for everyone using the hosted client. Moderators can still review the content through “show anyway” controls when auditing decisions. @@ -88,8 +88,8 @@ You can manage moderation directly from the client using the existing menus rend ### **5.5 Safety & Moderation thresholds** -- Open your profile avatar → **Profile** → **Safety & Moderation** to adjust blur and autoplay thresholds. Leave a field blank to return to the default (blur at ≥3 trusted reports, autoplay block at ≥2). -- These controls update immediately and sync with the moderation overlays applied by `moreMenuController` and `VideoCard` components. +- Open your profile avatar → **Profile** → **Safety & Moderation** to adjust blur and autoplay thresholds. Leave a field blank to return to the defaults exported from [`config/instance-config.js`](../config/instance-config.js) (`DEFAULT_BLUR_THRESHOLD`, `DEFAULT_AUTOPLAY_BLOCK_THRESHOLD`, `DEFAULT_TRUSTED_MUTE_HIDE_THRESHOLD`, `DEFAULT_TRUSTED_SPAM_HIDE_THRESHOLD`). The hosted client currently ships with blur at 3 trusted reports, autoplay block at 2, trusted mute hide at 1, and trusted spam hide at 3. +- These controls update immediately and sync with the moderation overlays applied by `moreMenuController` and `VideoCard` components. Operators can edit the config file to change the baseline before deploying their own instance. ## **6. Appeals and Feedback** diff --git a/docs/moderation/README.md b/docs/moderation/README.md index 05960c68..c753ebfd 100644 --- a/docs/moderation/README.md +++ b/docs/moderation/README.md @@ -31,15 +31,17 @@ bitvid is follow-centric. Your Home feed comes from people you follow (F1). Disc Thread new moderation behaviors through the same service → stage → app → UI flow above. Extending the existing layers keeps overrides, feature flags, and QA hooks consistent—avoid spinning up parallel moderation modules unless the architecture document explicitly calls for it. ## Defaults (policy) -- Blur video thumbnails if **≥ 3** F1 friends report `nudity`. -- Disable autoplay preview if **≥ 2** F1 friends report `nudity`. -- Hide videos when **≥ 1** trusted contact mutes the author (`TRUSTED_MUTE_HIDE_THRESHOLD`). Cards render with `data-moderation-hidden="true"`, the badge reads `Hidden · 1 trusted mute`, and a "Show anyway" button becomes available. -- Hide videos when **≥ 3** trusted contacts file spam reports (`TRUSTED_SPAM_HIDE_THRESHOLD`). The badge copy escalates to `Hidden · 3 trusted spam reports` and the card stays hidden until the viewer overrides it. +- Blur video thumbnails when trusted `nudity` reports meet `DEFAULT_BLUR_THRESHOLD`. +- Disable autoplay preview when the trusted `nudity` report count reaches `DEFAULT_AUTOPLAY_BLOCK_THRESHOLD`. +- Hide videos when `DEFAULT_TRUSTED_MUTE_HIDE_THRESHOLD` trusted contacts mute the author. Cards render with `data-moderation-hidden="true"`, the badge reads `Hidden · {count} trusted mute(s)`, and a "Show anyway" button becomes available. +- Hide videos when `DEFAULT_TRUSTED_SPAM_HIDE_THRESHOLD` trusted contacts file spam reports. The badge copy escalates to `Hidden · {count} trusted spam report(s)` and the card stays hidden until the viewer overrides it. - Downrank author when any F1 has them in mute list (10000). - Opt-in admin lists (30000 with `d=bitvid:admin:*`) can hard-hide content. - Trust seeds from `DEFAULT_TRUST_SEED_NPUBS` count as baseline F1 contacts (toggle via `FEATURE_TRUST_SEEDS`). -> You can override all defaults in **Settings → Safety & Moderation**. +> Default values live in [`config/instance-config.js`](../../config/instance-config.js). The upstream configuration ships with blur at 3 trusted reports, autoplay block at 2, trusted mute hide at 1, and trusted spam hide at 3. + +> You can override all defaults in **Settings → Safety & Moderation** or by editing the exports in [`config/instance-config.js`](../../config/instance-config.js) before deploying your instance. ### Hide thresholds & feature flags @@ -62,12 +64,12 @@ New operators should also note that fresh viewer accounts automatically inherit ### Safety & Moderation controls -The profile modal now exposes blur, autoplay, and hide thresholds so operators can dial in stricter or more permissive behavior. Enter a non-negative whole number to override the default or leave the field blank to fall back to the baseline values above. Adjustments are stored locally, applied immediately to the active feed, and rehydrate on every load. +The profile modal now exposes blur, autoplay, and hide thresholds so operators can dial in stricter or more permissive behavior. Enter a non-negative whole number to override the default or leave the field blank to fall back to the baseline values from [`config/instance-config.js`](../../config/instance-config.js) (`DEFAULT_BLUR_THRESHOLD`, `DEFAULT_AUTOPLAY_BLOCK_THRESHOLD`, `DEFAULT_TRUSTED_MUTE_HIDE_THRESHOLD`, `DEFAULT_TRUSTED_SPAM_HIDE_THRESHOLD`). Adjustments are stored locally, applied immediately to the active feed, and rehydrate on every load. Update the config file to change the per-instance defaults that new viewers see. ### Runtime flags -- `TRUSTED_MUTE_HIDE_THRESHOLD` — numeric default for the trusted mute hide control (default `1`). -- `TRUSTED_SPAM_HIDE_THRESHOLD` — numeric default for the trusted spam hide control (default `3`). +- `TRUSTED_MUTE_HIDE_THRESHOLD` — numeric default for the trusted mute hide control. Initialized from `DEFAULT_TRUSTED_MUTE_HIDE_THRESHOLD` in [`config/instance-config.js`](../../config/instance-config.js) (upstream ships with `1`). +- `TRUSTED_SPAM_HIDE_THRESHOLD` — numeric default for the trusted spam hide control. Initialized from `DEFAULT_TRUSTED_SPAM_HIDE_THRESHOLD` in [`config/instance-config.js`](../../config/instance-config.js) (upstream ships with `3`). - `FEATURE_TRUSTED_HIDE_CONTROLS` — boolean toggle to hide/show the new trusted hide controls in the UI (default `true`). ## Files in this folder diff --git a/docs/moderation/fixtures/index.html b/docs/moderation/fixtures/index.html index 578b8f11..52769576 100644 --- a/docs/moderation/fixtures/index.html +++ b/docs/moderation/fixtures/index.html @@ -50,10 +50,13 @@ import moderationService from "/js/services/moderationService.js"; const STORAGE_KEY = "bitvid:moderation:fixture-overrides"; + // These fixtures mirror the upstream defaults in config/instance-config.js. + // Update the numbers alongside DEFAULT_BLUR_THRESHOLD, DEFAULT_AUTOPLAY_BLOCK_THRESHOLD, + // DEFAULT_TRUSTED_MUTE_HIDE_THRESHOLD, and DEFAULT_TRUSTED_SPAM_HIDE_THRESHOLD when they change. const fixtures = [ { key: "blur-threshold", - heading: "Blur threshold (≥3 trusted nudity reports)", + heading: "Blur threshold (config default: ≥3 trusted nudity reports)", description: "Card should be blurred and autoplay disabled until the viewer overrides it.", thresholds: { blur: 3, autoplay: 2 }, @@ -81,7 +84,7 @@ }, { key: "autoplay-threshold", - heading: "Autoplay threshold (≥2 trusted nudity reports)", + heading: "Autoplay threshold (config default: ≥2 trusted nudity reports)", description: "Card should block autoplay but stay unblurred so users can inspect the thumbnail.", video: { @@ -127,7 +130,7 @@ }, { key: "trusted-report-hide", - heading: "Trusted report hide (≥3 spam reports)", + heading: "Trusted report hide (config default: ≥3 spam reports)", description: "Card should render hidden with spam copy until the viewer clicks \"Show anyway\".", enableOverride: true, diff --git a/docs/moderation/testing.md b/docs/moderation/testing.md index 08b6790d..051c396f 100644 --- a/docs/moderation/testing.md +++ b/docs/moderation/testing.md @@ -3,7 +3,7 @@ ## Quick checklist - [ ] F1 set cached and used for “trusted reports”. - Automated coverage: `tests/moderation/trusted-report-count.test.mjs` (run via `npm run test:unit`). -- [ ] Blur at ≥3 F1 reports of `nudity`; autoplay blocked at ≥2. +- [ ] Blur once trusted `nudity` reports meet `DEFAULT_BLUR_THRESHOLD`; autoplay blocks when the count reaches `DEFAULT_AUTOPLAY_BLOCK_THRESHOLD` (see [`config/instance-config.js`](../../config/instance-config.js) for defaults). - Automated coverage: `tests/visual/moderation.spec.ts` blur and autoplay scenarios (run via `npm run test:visual`). - [ ] Hide videos once trusted mute/spam thresholds are met. - Automated coverage: `tests/visual/moderation.spec.ts` trusted hide scenarios (run via `npm run test:visual`). @@ -40,8 +40,8 @@ Create three distinct F1 reporter keys; each sends: Expected: * After 1 report → no blur; autoplay allowed. -* After 2 reports → autoplay blocked. -* After 3 reports → thumbnail blurred; reason chip shown. At the spam threshold (`TRUSTED_SPAM_HIDE_THRESHOLD`) the card hides entirely and the badge copy switches to "Hidden · 3 trusted spam reports" until the viewer overrides it. +* Once reports reach `DEFAULT_AUTOPLAY_BLOCK_THRESHOLD` → autoplay blocked (upstream default: 2). +* Once reports reach `DEFAULT_BLUR_THRESHOLD` → thumbnail blurred; reason chip shown (upstream default: 3). At the spam threshold (`TRUSTED_SPAM_HIDE_THRESHOLD` in [`config/instance-config.js`](../../config/instance-config.js); upstream default: 3) the card hides entirely and the badge copy switches to "Hidden · {count} trusted spam report(s)" until the viewer overrides it. **Exercises:** [`ModerationService.ingestReportEvent()`](../../js/services/moderationService.js) and [`ModerationService.getTrustedReportSummary()`](../../js/services/moderationService.js) wire through [`createModerationStage()`](../../js/feedEngine/stages.js) so [`bitvidApp.decorateVideoModeration()`](../../js/app.js) can hand the summary to [`VideoCard.refreshModerationUi()`](../../js/ui/components/VideoCard.js). @@ -56,7 +56,7 @@ Expected: } ``` -Expected: author’s items are downranked/hidden for this viewer. When the mute threshold (`TRUSTED_MUTE_HIDE_THRESHOLD`) is met, cards render with "Hidden · 1 trusted mute" and require a "Show anyway" override to display content. +Expected: author’s items are downranked/hidden for this viewer. When the mute threshold (`TRUSTED_MUTE_HIDE_THRESHOLD`, sourced from `DEFAULT_TRUSTED_MUTE_HIDE_THRESHOLD` in [`config/instance-config.js`](../../config/instance-config.js); upstream default: 1) is met, cards render with "Hidden · {count} trusted mute(s)" and require a "Show anyway" override to display content. **Exercises:** [`ModerationService.ingestTrustedMuteEvent()`](../../js/services/moderationService.js) updates [`ModerationService.isAuthorMutedByTrusted()`](../../js/services/moderationService.js) / [`getTrustedMutersForAuthor()`](../../js/services/moderationService.js), which [`createModerationStage()`](../../js/feedEngine/stages.js) propagates to the video item before [`VideoCard.refreshModerationUi()`](../../js/ui/components/VideoCard.js) renders the mute state. diff --git a/docs/moderation/web-of-trust.md b/docs/moderation/web-of-trust.md index d27343e1..8dd1cebf 100644 --- a/docs/moderation/web-of-trust.md +++ b/docs/moderation/web-of-trust.md @@ -13,11 +13,13 @@ - (Optional) reputation score from a reputation source (e.g., PageRank/DVM) for **Discovery** only. ## Default thresholds (can be tuned) -- `blurThumbnail = trustedReportCount(event,'nudity') >= 3` -- `hideAutoplay = trustedReportCount(event,'nudity') >= 2` +- `blurThumbnail = trustedReportCount(event,'nudity') >= DEFAULT_BLUR_THRESHOLD` +- `hideAutoplay = trustedReportCount(event,'nudity') >= DEFAULT_AUTOPLAY_BLOCK_THRESHOLD` - `downrankIfMutedByF1 = true` -- `hideIfTrustedMuteCount(author) >= 1` -- `hideIfTrustedSpamReports(event) >= 3` +- `hideIfTrustedMuteCount(author) >= DEFAULT_TRUSTED_MUTE_HIDE_THRESHOLD` +- `hideIfTrustedSpamReports(event) >= DEFAULT_TRUSTED_SPAM_HIDE_THRESHOLD` + +Threshold constants are exported from [`config/instance-config.js`](../../config/instance-config.js) so operators can change the defaults without touching moderation code. The upstream repo ships with blur at 3, autoplay block at 2, trusted mute hide at 1, and trusted spam hide at 3. ### Why these numbers? - F1-only reports resist Sybil attacks. @@ -52,19 +54,19 @@ function trustedReportCount(eventId: string, type: Report['type'], viewerFollows } function shouldBlurThumb(eventId: string, ctx: Ctx): boolean { - return trustedReportCount(eventId, 'nudity', ctx.viewerFollows, ctx.reports) >= 3; + return trustedReportCount(eventId, 'nudity', ctx.viewerFollows, ctx.reports) >= DEFAULT_BLUR_THRESHOLD; } function shouldHideAutoplay(eventId: string, ctx: Ctx): boolean { - return trustedReportCount(eventId, 'nudity', ctx.viewerFollows, ctx.reports) >= 2; + return trustedReportCount(eventId, 'nudity', ctx.viewerFollows, ctx.reports) >= DEFAULT_AUTOPLAY_BLOCK_THRESHOLD; } function shouldHideForTrustedMute(authorHex: string, ctx: Ctx): boolean { - return ctx.trustedMuteCount(authorHex) >= 1; + return ctx.trustedMuteCount(authorHex) >= DEFAULT_TRUSTED_MUTE_HIDE_THRESHOLD; } function shouldHideForTrustedSpam(eventId: string, ctx: Ctx): boolean { - return trustedReportCount(eventId, 'spam', ctx.viewerFollows, ctx.reports) >= 3; + return trustedReportCount(eventId, 'spam', ctx.viewerFollows, ctx.reports) >= DEFAULT_TRUSTED_SPAM_HIDE_THRESHOLD; } ```