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://<TitleId>.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