VOIDERATION V22: INTEGRATED SCREECHVR SECURITY ARCHITECTURE Date: 2026-10-30 Status: Proposed architecture only. No implementation, dashboard or production changes. Evidence: V14-V21 focused reports; details remain subject to staging. GOAL AND LIMITS Protect account/economy permissions and make detections reviewable without claiming PUN Cloud is authoritative. All detections create a durable server-side incident and an owner alert. Incidents do not automatically mean cheating, restrictions or bans. Movement, tracking, collisions, tags and combat remain client-influenced under PUN 2; most are detectable at best. No permanent ban from weak signals. Preserve existing login, rooms/voice, Tag/Infection, PVP, movement, items, IAP, levels, /link, MOTD and CCU functionality. ARCHITECTURE DIAGRAM Quest client (Meta Platform + Unity/PlayFab SDK) | Meta user ID + one-use proof (future, server verified) | PlayFab login -> SessionTicket | GetPhotonAuthenticationToken(SessionTicket) -> Photon token | Photon auth params: username=PlayFabId, token=Photon token | Detection/session requests: short-lived session credential, not ticket v Cloudflare Worker (auth/API; secrets in Worker bindings) |-- PlayFab session verification / custom Photon-auth gate |-- D1: incidents, restrictions, review audit, alert outbox |-- Discord Bot API -> verified private owner channel |-- owner-authenticated API <-> dashboard bound to 127.0.0.1 only v Photon Cloud PUN 2 (relay, rooms, ownership; not trusted gameplay server) Current code lacks the proof-verification endpoint, D1, incident routes and heartbeat. Worker `/redeem` ticket validation is only for account linking. Diagram shows target state, not deployed components. COMPONENT RESPONSIBILITIES / IDENTITY - Unity/Meta: obtain Meta user ID and `Users.GetUserProof` only if adopted. A trusted backend must validate nonce with Meta S2S; client-reported ID or proof alone is not verification. Preserve existing PlayFab IDs, inventory and purchases via explicit account-link migration. Keep all service secrets out of the build. - PlayFab: login returns SessionTicket; Unity uses it for authenticated PlayFab API calls and to request a distinct Photon custom-auth token. Never substitute ticket for Photon token. `/Server/AuthenticateSessionTicket` accepts the PlayFab SessionTicket and returns server-derived PlayFabId; it does not validate a Photon token. - Photon: current Unity sends PlayFab ID as `username` and the PlayFab-issued Photon token as `token`. Do not trust username alone. Remove `PhotonVRManager`'s 20-second unauthenticated fallback; retry or show an error. Configure custom auth and disable anonymous clients for Realtime; verify Voice separately if a different app ID is used. EXACT WORKER-TO-PLAYFAB PHOTON AUTH GATE The official PlayFab integration configures Photon custom auth URL directly to `https://.playfabapi.com/photon/authenticate`; Unity obtains the token through PlayFab `GetPhotonAuthenticationToken` and passes username/token. That direct endpoint has no Voideration D1 restriction check. To enforce D1 restrictions, use a Worker/custom-auth adapter as Photon’s configured custom auth URL: 1. Photon calls Worker with the custom-auth request/parameters. 2. Worker forwards the PlayFab Photon authentication request to the official PlayFab `/photon/authenticate` endpoint using the exact documented wire contract, and accepts only PlayFab’s successful authenticated identity/result. 3. Worker derives the canonical PlayFab identity from that validated result, checks D1 active restriction/ban state, then returns Photon-compatible success only if allowed; otherwise return Photon’s documented rejection response. Important verification gate: the exact Photon-to-Worker request fields, how to forward them to PlayFab, expected response schema, and whether PlayFab permits this proxy arrangement must be confirmed from current provider documentation and a staging app before implementation. Do not treat a Photon token as a PlayFab session ticket or pass it to `AuthenticateSessionTicket`. If a safe compatible proxy cannot be proven, use a supported custom authentication service that validates the Photon token through the documented PlayFab contract; do not deploy a guessed adapter. Photon dashboard settings are unknown. Rejoin checks only work if anonymous access and all bypass connection paths are closed. MODERATOR, CURRENCY, PURCHASES - Roles are backend-owned (InternalData/role store); ordinary UserData, `Moderator`, local `AccountPref`, queue names and client UI are not authority. Every report, grant, ban/revoke action derives caller from validated session and checks role server-side. Never trust `ReporterId`, target authority or client moderator state. Audit owner actions. - Currency rewards/grants go through backend authorization, server-derived eligibility, bounded amounts, unique request IDs and durable dedupe. Disable direct client grant paths only after migrating all callers (ore, daily reward, bundles, ATM/IAP, shop). PlayFab client `PurchaseItem` checks expected price against catalog when configured; V08 custom `OnPurchaseItem` instead trusts `args.Price` and has no such protection (draft, not confirmed deployed). - Meta IAP is distinct from PlayFab virtual-currency purchases. Backend verifies Meta entitlement server-to-server, maps SKU/reward server-side, grants via idempotent transaction/outbox and consumes consumables only after fulfillment. Durable entitlements restore idempotently; do not use client-writable `OWNED_IAP` as transaction authority. Preserve products and entitlements. PHOTON GAMEPLAY + DETECTION - RPC receivers validate `PhotonMessageInfo.Sender`, view ownership, parameters, legal state, sequence and per-actor rate; prefer idempotent set-state over toggles; remove per-frame buffered events. This prevents accidental/cross-actor calls and replay, not a malicious owner lying about its own actions. - PUN ownership and MasterClient are client authority, not server truth. Position, controller pose, collision, hits, physics, tag results and wins cannot be securely adjudicated without authoritative simulation. Keep gorilla movement, welding and current modes responsive; telemetry produces flags only. - Calibrate speed/teleport/reach rules from clean traces per scene/mode/boost and network/tracking state. Source values include arm length 1.5 in identified prefabs, Tag touchRadius 0.15, and Speed_Boost maxJumpSpeed 6.5/15; these are not universal movement thresholds. Lag, tracking loss, frame spikes and stale samples lower confidence. No automatic permanent bans. HEARTBEAT / WORKER / D1 - Five-minute heartbeat is liveness only. Worker validates PlayFab ticket at session start, derives PlayFabId, issues opaque short-lived session credential and one-use random challenge. Each beat includes session credential, increasing sequence and current challenge. D1 atomically condition-checks active session, expiry, sequence and challenge while writing next sequence/last_seen/new challenge. No KV write per beat. A heartbeat cannot prove client integrity. - D1 stores minimal incidents, restrictions, audit transitions, session state and optional alert outbox. Unique IDs + conditional updates/transaction prevent duplicate incidents and concurrent owner decisions. Retain only necessary evidence and expire session/flag data by policy; no raw tickets, Photon tokens, proof nonces or indefinite pose streams. - Preserve existing Worker routes (`/redeem`, `/motd`, Photon webhooks, Discord interactions, `/export`, `/setup`, `/health`). `/updateplayers` currently ignores input; do not treat it as security telemetry. Keep per-route auth, schema/method limits and rate limits; migrate query-string secrets separately. - V19 sizing: heartbeat alone at 20/50/100 players is 5,760/14,400/28,800 requests and approximately one D1 update per beat per day. Cloudflare Free published quotas can fit 100 CCU in isolation, but Worker 100K requests/day and D1 read/write quotas are shared with existing traffic. Measure actual D1 `rows_read`/`rows_written`, incident rate and account headroom. €0 additional recurring is plausible on current Free plan, not guaranteed; plan/account limits and traffic are unknown. INCIDENTS, RESTRICTIONS, REVIEW, REJOIN Keep distinct state dimensions: - Incident `review_status`: `PendingReview`, `Approved`, `Rejected`. - Player restriction: `None`, `PendingReview` (temporary admission hold), `ConfirmedRestricted` (owner confirmed ongoing restriction), `Released`, or separately recorded `Banned`. - Approval semantics: owner `Approve/Clear` means the evidence does not justify continued restriction; it marks incident Approved and releases a PendingReview restriction. Owner `Reject/Cannot clear` marks incident Rejected, confirms violation for this workflow, and keeps restriction as ConfirmedRestricted. ConfirmedRestricted remains until owner explicitly releases it or issues a separate ban decision. This avoids using incident status as player access state. Owner only; every attempted/successful transition is audited with actor, reason, before/after, timestamp and request ID. Per-rule rejoin policy (initial conservative defaults; owner can revise after false-positive review): - Invalid/expired ticket: deny that authentication attempt; incident only if authenticated identity can be established. No account restriction based on unverified identity or service failure. - Repeated, server-verified replay/invalid heartbeat/session credential from a valid identified player: incident + alert; temporary PendingReview restriction may be required before rejoin only after repeated protocol violations, not one retry. Owner clear releases; rejection confirms restriction. Missed heartbeat or Worker outage never creates restriction. - Movement speed/acceleration, teleport-like samples, hand reach, tracking, latency, client-reported Tag overlap: incident + alert, rejoin allowed by default. No automatic restriction from these heuristics. - Repeated impossible Tag/Infection state transition: incident + alert; default rejoin allowed. Temporary review restriction only after corroborated, server-observable protocol abuse and owner policy; PUN cannot validate real hand collision. - Verified receipt/currency abuse or confirmed unauthorized server request: reject the transaction, create incident + alert. Do not block login by default; only owner may add temporary restriction after review. Permanent ban is separate. Rejoin: every new Photon authentication consults current restriction state, not client cache, room, actor number or IP. Custom auth is connection-time. A normal Worker request cannot reliably kick an already-connected PUN Cloud actor; do not claim otherwise. Existing restricted session may remain until disconnect. A cooperative client notice/request may be offered, but not trusted enforcement. Avoid room-wide disruption. Retest room rejoin, fresh login and Voice admission. Alerts/dashboard/privacy/outages: - Persist incident before alert. Send private, deduplicated Discord alert with incident ID, UTC time, rule/version, severity/confidence, concise evidence caveat, review status and restriction state; no tokens, receipts, raw IP or proof nonce. Verify channel privacy. Keep bot credentials in Worker secrets. - Local dashboard binds only to 127.0.0.1, owner-authenticates, uses CSRF-safe actions and calls Worker APIs (never direct D1 from browser). Do not host on public Pages or embed backend secrets. Audit every decision. - Discord failure leaves incident durable and alert outbox retries boundedly. D1/Worker/PlayFab failure is not player evidence. For new auth, fail closed with retryable service error while restriction state is unavailable; this can temporarily block all new sessions. Existing sessions are not mass-kicked. Never fall back to anonymous. Heartbeat errors only mark telemetry unavailable. PHASES AND RELEASE GATES 0. Verify deployed Worker/CloudScript, PlayFab policies, Photon auth configuration, app IDs, private Discord permissions, Cloudflare quotas and account migration. No behavior change until settings are known. 1. Identity/auth staging: implement/validate Meta proof binding if approved; prove Worker-to-PlayFab Photon proxy contract and Voice flow. Gate: existing accounts/data preserved; token failure/outage never yields anonymous access. 2. Economy/role migration: trusted role store, server-authorized grants, purchase receipt/catalog flow, idempotency and legacy data migration. Gate: currency, all existing cosmetics/bundles/IAP/restoration work exactly once. 3. Incident/review core: D1 schema, server-side detection intake, owner-only transitions/audit, private alert outbox, loopback dashboard. Gate: every test detection logs/alerts; forged client approval denied; duplicates safe. 4. Connection restrictions/heartbeat: new-session check, 5-minute liveness, replay protection and no anonymous fallback. Gate: restricted reconnect denied, approved release restored, outages retry safely; 100 CCU load/quota measured. 5. Photon gameplay hardening and detection: RPC sender/state/rate checks; clean movement baselines under boosts, tracking loss and lag. Gate: Tag/Infection, movement, welding, PVP, horror levels, voice, rooms, transitions and late join regressions pass; no permanent auto-ban from heuristics. 6. Gradual release, monitoring, rollback and owner review runbook. Dedicated authoritative server remains a separate cost/architecture decision. UNRESOLVED DASHBOARD / POLICY SETTINGS - Photon Custom Server URL/provider, anonymous clients setting, failure behavior, PlayFab Photon add-on/App ID association, Voice app ID/auth configuration, custom-auth proxy compatibility and targeted-disconnect options. - PlayFab Client API policies, deployed CloudScript/Azure Functions, user-data permissions, catalog/receipt validation, bans and ticket behavior. - Meta DUC/IAP/User ID eligibility and current product/SKU setup. - Cloudflare plan, Workers/D1 aggregate usage, bindings, secrets, alert limits; Discord admin channel effective permissions and bot permissions. - Owner policy: review SLA, temporary restriction expiry, evidence retention, appeal path and whether confirmed restriction or ban is appropriate. Evidence reports: `research/SCREECHVR_AUTH_REVIEW_V15_2026-10-30.txt`; `research/SCREECHVR_MOD_CURRENCY_REVIEW_V16_2026-10-30.txt`; `research/SCREECHVR_IAP_REVIEW_V17_2026-10-30.txt`; `research/SCREECHVR_PHOTON_SECURITY_V18_2026-10-30.txt`; `research/SCREECHVR_WORKER_HEARTBEAT_V19_2026-10-30.txt`; `research/SCREECHVR_MOVEMENT_DETECTION_V20_2026-10-30.txt`; `research/SCREECHVR_MANUAL_REVIEW_V21_2026-10-30.txt`. V14 remains unchanged. This is a concise integration plan, not an implementation specification for unverified provider contracts. END OF REPORT