Tactical Action · Features

Trust, Safety & Compliance

A focused page within the Tactical Action Features documentation. The full map and every sibling page live in the Features hub.

4sections10 minread1diagram

On this page

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, and the section hub is ../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-reviewreview 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).

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.

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 BuildDpiaSummaryCardsAI 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#