# Trust, Safety & Compliance

V4 ships six tactical-action cells under one account, one operator roster, one
currency ledger, and one progression record, across nine day-one platforms — and
the moment any of that goes live it inherits three obligations that a tactical
shooter cannot wave off. **A competitive ladder rots the day cheating goes
unanswered**, so a Search & Destroy round, an RTS 1v1, and a contract
leaderboard run all have to be defended by the same evidence-gated anti-cheat.
**A regulator fines, and a platform holder refuses the build, over a deletion
that fails to cascade or a data-safety form that is missing** — so a
right-to-be- forgotten request has to reach every place a player's identifiers
landed, including the cloud replays other people are still watching. And **a
minor must never be one toggle away from voice chat with a stranger**, so the
age gate and the parental surface are computed defaults, not a recommended
setting. The defining choice this page documents is also the first thing to
state honestly: where V2 and V3 reach sideways into the sister monorepos and
_compose_ the shared `@themis`, `@kuanyin`, and `@aphrodite` trust-safety
packages through thin surfaces, **V4 reimplements** the enforcement seams
natively — as Unreal C++ subsystems and Rust services that live exactly where
the gameplay does. This is the player-and-policy face of that decision; the
engine-internal companion is
[../architecture/security-compliance-launch.md](../architecture/security-compliance-launch.md),
and the section hub is [../V4_features.md](../V4_features.md).

## What ships, honestly

The trust-safety and compliance logic is **real, domain-specific code**, not a
policy stub dressed as a feature.

- **The compliance brain is a substantive Unreal subsystem.**
  `V4/ue/Source/V4OnlineServices/Private/V4ComplianceSubsystem.cpp` (~490 lines)
  builds 30-day DSAR export cases and 45-day deletion cases, evaluates an
  under-13 / teen / adult age gate, resolves per-region data-residency rules,
  emits DSA statement-of-reasons logs, drives an eight-region feature-flag
  matrix, and self-validates through `ValidatePrivacyByDesignCoverage` and
  `ValidateLaunchCoverage`. It is exercised by
  `V4Tests/Private/V4ComplianceTests/ComplianceSpec.cpp`.
- **Anti-cheat is computed, not asserted.**
  `V4/ue/Source/V4Netcode/Private/V4AntiCheatSubsystem.cpp` (187 lines) computes
  behavioral-detection thresholds, a 0–100 trust score, a ten-tier ban ladder,
  ladder-replay integrity validation, and an appeal validator, tested by
  `V4AntiCheatTests/AntiCheatSpec.cpp`.
- **Moderation and the replay scrubber are real Rust.**
  `apps/v4/online-services/src/lib.rs` carries a `CaseService` (report intake
  with mandatory clip evidence, the two-stage content scan, speech review, ban
  issuance, appeals) and a right-to-be-forgotten
  `scrub_deleted_account_from_replay` that mutates pawn tracks and returns a
  typed `ReplayScrubResult`. Both ride the Axum router in `src/http.rs`, and the
  scrubber is backed by the 388-line `ReplayModeSpec.cpp`.
- **A machine-checked gate ties it together.**
  `apps/v4/scripts/src/v4-privacy-by-design-check.mjs` reads the C++ source, the
  Rust source, the legal JSON artifacts, the service manifest, _and_ the
  docs/TODOs, and fails CI if any DPIA, jurisdiction note, region flag, or
  scrubber wiring is absent — so the prose on this page cannot drift from the
  code behind it.

Three honest qualifications. **First**, the divergence above is not a footnote:
the shared `@themis/transparency`, `@kuanyin`, and `@aphrodite/*` packages are
real and present in `libs/`, but **V4 does not import them**. V4 reimplements
identity-aware compliance, residency, age-gating, and anti-cheat in-engine on
purpose, and the only cross-monorepo touchpoints are _boundary hooks_, not
runtime dependencies — the Rust moderation service accepts a
`lilith_rights_match` / `lilith_persona_policy_match` input and cascades a
`lilith_rights_takedown_cascade`, and the sub-processor registry names
third-party processors (Perspective API, Whisper). **Second**, V4 owns no cooked
binary content; every Unreal asset is a text `.v4asset.json` stand-in, including
`DA_ComplianceDeep` and `DA_ModerationPipeline`, so "the kernel driver" and "the
shipped restricted build" are configured and validated here but not compiled in
this repository. **Third**, what is honestly _policy_ rather than code is the
cert obligation set itself — the rating-board matrix, the regulatory SLAs (GDPR
30 days, CCPA 45), and the public portal URLs — the enforcement seams are coded,
the obligations are documented and audited.

## Trust & safety

Reports and sanctions in V4 are **identical across all nine platforms** — a slur
or a snap-aim is not a platform-specific event — and every step from a report to
a permanent ban is built to produce an auditable, appealable trail.

### Cross-platform reporting and the evidence requirement

A report is useless to a moderator without context, so V4 attaches it at the
source. `CaseService::submit_player_report` is **fail-closed**: it returns
`false` unless the `PlayerReport` carries a non-empty report id, reporter,
target, and reason **and** a `GameplayClipEvidence` that is actually `attached`,
has a non-empty `cloud_uri`, and a non-zero `duration_seconds`. You cannot file
a report on an empty record — the last segment of gameplay recording rides along
as evidence so a moderator has the moment without the reporter assembling it. A
report advances Submitted → Triaged → (Actioned | Dismissed), and the reporter
is told only that an outcome was reached, never the specific sanction, which is
the coded answer to retaliation and report-farming. Every action resolves to an
appeal at a stable, signed URL: `CaseService::appeal_portal_url` returns
`https://support.v4.game/appeal?sanctionId={id}`, and the appeal is reviewed
against the logged evidence by a different reviewer than the one who issued it.

### Anti-cheat: layered, evidence-gated, never auto-permanent

No single layer is trusted alone, and the design constraint that shapes all of
them is that **automated detection never issues a permanent ban**. The
kernel-mode client is configured by `BuildDefaultEACConfig` (Easy Anti-Cheat
across Windows, Linux, PS5, XSX, XSS) and `ValidateEACConfig` rejects any
platform that does not require kernel mode and protect both ranked PvP and
battle royale. Ladder integrity is checked by `ValidateLadderReplay`, which
refuses a replay that is not ladder-eligible, has zero duration, is missing any
of its three integrity hashes, or — tellingly — whose replay, server-state, and
input hashes are not **independently produced** (equal hashes are rejected as
forged).

Behavioral detection is a real classifier, not a coin flip. `EvaluateBehavior`
flags _impossible recoil_ when a sample fired ≥ 20 shots at a recoil-control
score ≥ 0.98, and a _wallhack pattern_ when hidden-target pre-aim count ≥ 3 or
wall-visibility ratio ≥ 0.35, then grades severity **9** (both), **7** (recoil),
or **6** (wallhack). `RouteToAntiCheatService` sends severity ≥ 8 to
`anti-cheat.eac-review` and everything else to `anti-cheat.live-review` —
**review queues, not auto-bans**. `CalculateTrustScore` computes
`100 − strikes×25 − investigations×10` (clamped 0–100), pools any account below
50 into `bLowTrustPool` so suspected cheaters matchmake together while a case is
built, and a missing account id collapses straight to 0 / low-trust.
`BuildBanTiers` lays out the ten-step ladder — Mute (24h/7d/30d), Match ban
(1/7/30d), Cell ban (1/7/30d), and a permanent Account ban — and
`ValidateAppeal` gates every appeal on a present appeal/sanction/account id and
a statement of at least 24 characters. A permanent ban therefore requires replay
evidence **plus** telemetry **plus** manual review, by construction.

### Speech moderation and the two-stage content gate

Speech review composes two real sub-processors. `CaseService::review_speech`
routes text to Perspective scoring and voice (via a clip id + Whisper
transcript) separately, applies the Lilith persona policy on a match **or** a
literal slur in either channel, requires human review at a Perspective toxicity
or Whisper abuse score ≥ 70, and recommends an automatic mute at ≥ 90 — so
clear-cut cases mute without waiting on a queue while everything ambiguous
escalates to a person. User-generated content runs `CaseService::scan_content`,
a two-stage gate that **blocks distribution** at nudity ≥ 85 or violence ≥ 90,
queues **human review** at nudity ≥ 60 / violence ≥ 65 or any community report,
and — when a `lilith_rights_match` fires — appends
`lilith_rights_takedown_cascade` and `queue_appeal`. That cross-cell rights hook
is the same gate the community gallery and creator workshop submissions pass
through (detailed on
[Content, Creator & Community](./content-creator-and-community.md)).

```mermaid
flowchart TB
  R[Player report<br/>+ attached gameplay clip] --> CS["CaseService::submit_player_report<br/>(fail-closed on missing evidence)"]
  SP[Voice / text] --> SR["review_speech<br/>Perspective ≥70 review · ≥90 mute"]
  UGC[Workshop / gallery] --> SC["scan_content<br/>nudity ≥85 block · ≥60 review"]
  BEH[Match telemetry] --> AC["EvaluateBehavior → severity 6/7/9"]
  CS --> Q[Moderator queue]
  SR --> Q
  SC --> Q
  AC --> RT["RouteToAntiCheatService<br/>review-only, never auto-ban"] --> Q
  Q --> TS["CalculateTrustScore<br/>low-trust pooling"]
  Q --> BAN["BuildBanTiers<br/>10-tier ladder"]
  BAN --> SOR["BuildStatementOfReasons<br/>(DSA)"]
  SOR --> TR["BuildTransparencyReport"]
  BAN --> AP["appeal_portal_url<br/>different reviewer"]
```

## Compliance

Privacy and platform-regulation obligations are not a launch-week scramble; in
V4 they are computed and gated, and **all compliance gates green is a launch
exit criterion** verified by `ValidateLaunchCoverage`.

### Age gate, parental controls, and the under-13 restricted build

`EvaluateAgeGate(BirthYear, CurrentYear, bGuardianVerified)` is the spine of
youth safety. Under 13 it demands the COPPA family-link flow and, only with a
verified guardian, routes the account to a separate restricted build
(`V4.Restricted.Under13`) whose flags disable voice, text, UGC, and purchases.
Ages 13–17 resolve `Teen`; 18+ resolve `Adult`. The parental surface is
`BuildDefaultParentalControlPolicy`: a fixed set of per-day time-played caps
(`{15, 30, 60, 120, 240, 0}` minutes, where 0 is "no cap"), a hard under-18
monthly spend ceiling, and — the load-bearing default — voice **and** text chat
off for under-18 accounts with `bParentOptInRequired` set, so a minor cannot
click into open comms. Because V4 ships no random-roll or gambling-adjacent
purchase mechanic in any region, the under-18 spend restriction is a tightening
of an already loot-box-free store, not a patch over one.

### Data-subject rights, residency, and the replay scrubber

`BuildDsarExportCase` mints a 30-day case that exports the account to a JSON
archive at `https://support.v4.game/dsar`; `BuildDeletionCase` mints a 45-day
`Purge`. Both are routed by `ResolveDataResidencyRule`, which maps a region to
its host and a paired KMS region — EU → `eu-central-1` / `eu-kms-v4`, PRC →
`cn-north-1` / `prc-kms-v4`, US → `us-east-1` / `us-kms-v4` — so account data is
encrypted and stored in-region. The hardest part of a deletion is the data a
deleted player left **in other people's replays**, and that is where the
right-to-be-forgotten replay scrubber earns its place.
`scrub_deleted_account_from_replay` walks a replay's participant pawns, clears
the matching `account_id`, swaps the visible skin to
`Skin.Anonymous.DeletedAccount`, marks each track removed, and — if the deleted
account _owned_ the replay — clears the owner, unstars it, and resets retention
to the default 14-day cloud tier. Crucially it **retains the match-integrity
frames**, so playback stays valid for everyone else while the deleted player is
anonymized. It returns a typed `ReplayScrubDecision` (`Scrubbed` / `NotFound` /
`NoAccountFootprint` / `InvalidRequest`), and the same contract has a second
runtime in the Unreal replay plugin
(`UV4ReplayCloudArchive::ScrubDeletedAccountFromReplay`), detailed on
[Modes: Training, Replay, Signature & AI Director](./modes-training-replay-signature-and-ai-director.md).

### Per-region flags, DSA reasons, sub-processors, and AI DPIAs

`BuildRegionFeatureFlagMatrix` resolves an eight-region matrix in which China
defaults voice chat **off** and runs a cosmetic-only economy (disabling
`VoiceChat.DefaultOn` and `Economy.RealMoneyAdvantage`), while Korea and India
also resolve cosmetic-only and the remaining regions run the full economy —
`ResolveRegionFeatureFlags` normalizes aliases (DE/FR/ES → EU, GB → UK) so the
runtime default is never a guess. Every moderation action emits a DSA
statement-of-reasons via `BuildStatementOfReasons`, whose `bLogged` flag is true
only when actor, target, reason code, evidence id, and the statement text are
all present, and `BuildTransparencyReport` rolls those into a periodic public
count. `BuildSubprocessorRegistryMaintenance` encodes the registry's discipline:
refresh on `ContractChange`, contract review required, and opt-in subscribers in
eight regional topics notified within 30 days. Three AI features each carry a
published, annually refreshed DPIA summary card via `BuildDpiaSummaryCards` —
**AI Auto-Moderation**, **Behavior Detection**, and **In-Game Commentary** —
backed by `V4/legal/dpia-summary-cards.json`. The commentary DPIA is the most
careful: its mitigations include watermarking and, for opt-in personal
voice-clone commentary, a double-consent path (player **plus** friend likeness
consent) checked against a revocation ledger — a documented P3 commitment whose
supporting consent/likeness types live in the replay-mode runtime, not a
fabricated "done."

### The privacy-by-design gate

What keeps this section honest is `v4-privacy-by-design-check.mjs`. It cross-
checks the compliance asset against the legal JSON (`subprocessors.json`,
`region-feature-flags.json`, `replay-scrubber-policy.json`,
`per-jurisdiction-compliance-notes.json` — all 50 US states plus EU/UK/BR/JP/KR/
IN/CN), greps the C++ and Rust source for the required tokens
(`ScrubDeletedAccountFromReplay`, `scrub_deleted_account_from_replay`,
`Skin.Anonymous.DeletedAccount`, `ValidatePrivacyByDesignCoverage`), confirms
the `service-manifest.json` advertises the matching capabilities, and asserts
the docs and TODOs name them. In-engine, `ValidateCompliancePolicyAsset` and
`ValidatePrivacyByDesignCoverage` perform the same audit on live objects: 30-day
DSAR, 45-day delete, the under-13 restricted build, three DPIAs, three residency
rules, the China voice-off / cosmetic-only flags, and the anonymous- skin scrub
default. A compliance claim that is not backed by code does not pass CI, which
is precisely the bar this page is written to.

## Where this connects

- **Sideways:**
  [Platforms, Operations & Hardware](./platforms-operations-and-hardware.md) —
  per-platform certification (Sony TRC, Microsoft XR, Nintendo), the region
  rating variants, day-one patch, and the live-ops surfaces these gates protect.
- **Sideways:** [Narrative, Extras & Launch](./narrative-extras-and-launch.md) —
  the launch exit criteria this compliance posture is one of, and the
  AI-commentary and creator extras the DPIAs cover.
- **Sideways:**
  [Content, Creator & Community](./content-creator-and-community.md) (the
  gallery and workshop the two-stage `scan_content` gate moderates) and
  [Modes: Training, Replay, Signature & AI Director](./modes-training-replay-signature-and-ai-director.md)
  (the replay vault the scrubber mutates).
- **Down to the engine:**
  [../architecture/security-compliance-launch.md](../architecture/security-compliance-launch.md)
  — the `UV4ComplianceSubsystem` / `UV4AntiCheatSubsystem` internals, the Rust
  service tier, the 616-line launch-readiness validator, and the honest record
  of why V4 reimplements the shared trust-safety plane rather than composing it.
- The feature hub: [../V4_features.md](../V4_features.md).
