VOIDERATION V25: DUAL BAN ENFORCEMENT Date: 2026-10-30 Scope: Research only; no code, API, dashboard or production changes. Baseline: V22 architecture, V23 dashboard design, V24 PlayFab ban design and local Worker/Unity source. Proposed systems are not implemented. DECISION Maintain two independent ban authorities: 1. PlayFab ban: created/revoked through trusted PlayFab Admin API; PlayFab is authoritative for its status. 2. Voideration ban: independently persisted in Cloudflare D1 and enforced by Voideration's trusted authentication backend. D1 is authoritative for this ban, not a mirror of PlayFab. The Photon admission gate denies if either effective ban is active, or if a separate PendingReview restriction is active. Incident status, review holds, PlayFab bans and Voideration bans are separate records/states. Local dashboard is the owner control plane; Discord remains optional notification only. CURRENTLY CONFIRMED / NOT IMPLEMENTED - Unity obtains a PlayFab Photon authentication token using a PlayFab session and sends PlayFabId as `username` with the Photon token as `token`. These credentials are not interchangeable. Do not pass Photon token to `AuthenticateSessionTicket` or trust the submitted username. - `PhotonVRManager` has unauthenticated fallback after waiting for auth. Close this and all other anonymous connect paths before claiming ban enforcement. - Existing Worker has Discord owner ban commands that call PlayFab Admin BanUsers, store one BanId in KV and call a singular revoke route. This is not robust multi-ban/D1/dashboard handling; verify revoke route against current documented API. No D1 ban API or Photon custom-auth proxy is implemented. - No evidence PlayFab ban automatically disconnects an existing PUN Cloud actor. Connection-time custom auth denies future admission; targeted live-session removal through an ordinary Worker request is not established. - Photon docs describe custom authentication at connection and dashboard controls for custom auth/anonymous clients. Exact proxy contract, dashboard values, repeat auth behavior for quick rejoin/Voice, and status response are UNVERIFIED. No dashboard access performed. AUTHENTICATION / API DESIGN Target sequence for every fresh Photon authentication/reconnect: 1. Photon calls the configured custom-auth endpoint (proposed Worker adapter) with Photon custom-auth parameters. The Worker validates the Photon token through the supported PlayFab Photon auth contract. It must derive canonical PlayFabId from a trusted validation result, not `username`. 2. For the validated identity, query effective PlayFab ban state with trusted server-side PlayFab Admin API `GetUserBans` and Voideration ban/restriction state in D1. Deny if either ban is active or a configured review restriction is active. Return Photon-documented rejection; do not join, rejoin or use an anonymous fallback. 3. Allow only after both authoritative checks succeed and both report clear. On provider/D1 timeout, malformed result or identity ambiguity, fail closed with retryable auth-service denial, not a ban verdict. Never cache an allow decision in the client. PlayFab API responsibilities: - `BanUsers`: apply PlayFab ban with target PlayFabId, reason and chosen duration; store every returned BanId and expiry. Title Secret Key remains in Worker secret binding only. - `GetUserBans`: query PlayFab source of truth for effective status. Do not use `Active=true` alone: account for revocation and UTC `Expires`; permanent means no expiry only where API contract says so. Multiple ban records can exist. - `RevokeBans`: revoke specifically selected BanId(s); avoid revoke-all unless owner explicitly requests it. Verify existing Worker route/contract. - PlayFab Photon auth: `GetPhotonAuthenticationToken` produces a Photon custom auth token; Photon custom auth typically validates it through PlayFab's `/photon/authenticate` integration. A Worker adapter must forward/validate the exact provider contract and return Photon-compatible result. The request, response schema, identity availability and proxy support are a staging blocker; no guessed implementation. Important identity edge: if PlayFab's Photon auth endpoint rejects a banned user before returning canonical identity, the Worker cannot safely trust the supplied username to query D1 for that same account. The PlayFab rejection already blocks admission, but it may prevent evaluating the second system or displaying both statuses. Prove a supported way to validate the credential and obtain canonical identity even for banned accounts, or document that denial short-circuits after the authoritative PlayFab rejection. Do not weaken identity validation to force both lookups. For ban display after PlayFab login rejection, V24's candidate is Meta User Proof + Worker server-to-server verification + verified Meta/PlayFab binding; eligibility and mapping are unverified. Never accept client PlayFabId. BAN, INCIDENT AND REVIEW STATES - Incident: `PendingReview`, `Approved`, `Rejected/Dismissed`, with timestamps, evidence and history. Dismiss false positive closes incident without ban. - PendingReview restriction: separate temporary admission hold; owner Approve clears it. It is not a ban and cannot be confused with one. - PlayFab ban: separate records with PlayFab BanId(s), reason, owner, issue time, start/expiry/permanent indicator, provider response and active/revoked/expired state reconciled against PlayFab. - Voideration ban: D1 record with independent ID, reason, issuer, start/expiry, created/updated/revoked timestamps, status and action history. It remains active until expiry or owner revocation. It does not create a PlayFab ban unless owner explicitly chooses both systems. - Dashboard must show each system separately: status, reason, duration, UTC timestamps, BanId/Voideration ID, pending/uncertain provider result, and whether Photon admission is currently blocked. Never merge into an ambiguous “banned” boolean. - Owner actions: issue, extend, revoke, review either ban system independently; require password re-auth, reason, duration, confirmation and idempotency key. Every attempted/successful/failed action is audited. “Extend” updates selected system only; PlayFab extension may require new ban/revoke-and-reissue depending on API capabilities, so verify semantics before UI promises it can mutate a specific BanId. CONSISTENCY, CACHING, FAILURES AND RECOVERY - No atomic transaction spans PlayFab and D1. For PlayFab ban: write D1 operation intent/idempotency key, call PlayFab, persist all returned BanIds and result; reconcile uncertain timeout or D1 failure with `GetUserBans` before retrying. Do not show success until provider confirmation. For Voideration ban, commit D1 record and audit transactionally; D1 failure means action failed. - Do not synchronize one ban into the other automatically. Dashboard offers explicit one-system or both-system actions. If owner chooses both, show partial-success state and reconcile each side independently. Revocation in one system does not release the other ban or review restriction. - Authentication: safest initial design does no positive caching of “clear” ban status; query both systems for every new Photon admission. Any temporary cache must be short, keyed by canonical ID, invalidate on owner action, and never outlive the shortest ban/credential validity; stale allow is a bypass risk. - If either check is unavailable, deny new authentication with retry/backoff and clear service-error UI; do not label as ban, set a ban record, or silently allow. Existing sessions are not automatically disconnected by this check. If the Worker/D1/PlayFab outage is prolonged, game admission may be unavailable; this is the explicit fail-closed tradeoff required to guarantee both checks. - Reconcile periodically and on owner review: query PlayFab GetUserBans against D1 projection; detect missing/expired/revoked/multiple bans and repair with audit. D1 is authoritative for Voideration bans, so backup/restore must preserve their state. Back up D1 and test restore; a stale restore can resurrect or drop a Voideration ban. PlayFab bans must be re-queried after restore. - Live Photon connections: neither PlayFab ban nor D1 admission check proves a connected actor is kicked. Cooperative client may receive a ban notice and disconnect, but cannot be trusted. Do not disrupt whole rooms. UI/status should say “blocks next authentication; existing session may remain” unless disconnect is separately confirmed. BADGUYSCENE / DISPLAY On definitive active PlayFab or Voideration ban, Unity routes to `BadGuyScene` and displays issuing system, sanitized reason, permanent or remaining duration, and UTC expiry if finite. If both active, display both separately. Login errors without confirmed ban and backend timeouts use retryable error UI, not ban scene. For a banned account whose PlayFab login is rejected, use provider-supplied `AccountBanned` details if reliable, or V24's Meta proof verification + trusted account binding endpoint. Do not create an unauthenticated Worker lookup by client-supplied PlayFabId/username. This identity/display path is not yet proven. COST / FUNCTIONALITY €0 additional recurring remains a target, not guaranteed. D1 and Worker can fit within Free quotas only if existing Worker traffic plus heartbeat, polling, two ban checks per authentication, management calls, reconciliation and logs stay within actual account limits. Measure at 100 CCU; V19's ~28,800 heartbeat Worker requests and base D1 row updates/day do not include dual lookups/auth traffic. Avoid per-frame writes and frequent polling. No extra hosted service is needed, but availability now depends on both providers. A strict fail-closed outage can block new gameplay without added provider redundancy. IMPLEMENTATION GATES / TESTS 1. Verify exact PlayFab Admin API duration, BanUsers/GetUserBans/RevokeBans semantics and Photon custom-auth request/response contract using docs and staging. Verify whether canonical identity is available for banned accounts. 2. Inventory current Worker ban commands/BanId KV and migrate/reconcile records; inspect Photon Realtime/Voice app settings and disable every anonymous path only after staging. Do not touch production in this research phase. 3. Test each independently: PlayFab ban only denies; D1 ban only denies; both active show both; expired/revoked one still denies if other active; revoke one leaves the other enforced; PendingReview hold is separate and clears on approve. 4. Test auth token vs SessionTicket separation, forged username, unknown identity, invalid/expired credentials, quick rejoin/app restart/room rejoin/region and Voice reconnect, and no direct Photon connect bypass. 5. Simulate PlayFab/D1/Worker outage, timeouts after provider success, malformed responses, D1 write failure, duplicate owner actions, stale cache, clock/expiry boundaries, restore and reconciliation. No fail-open and no false ban label. 6. Test BadGuyScene reason/system/expiry for each ban, both bans, permanent, expired, missing reason, login rejection and service outage. Test existing live session limitation; never report kicked unless observed. 7. Preserve Oculus/PCDEBUG login, inventory, currency/purchases, `/link`, MOTD, CCU, Voice, Tag/Infection, PVP, locomotion, Sandbox/welding, horror hub/LVL1-7, scene transitions and reconnect behavior. Load-test Worker/D1 at 100 CCU and verify actual quota use before rollout. UNRESOLVED / BLOCKERS - Exact Photon-to-Worker-to-PlayFab proxy support and canonical identity on failed PlayFab authentication; all Photon/Voice app settings and quick-rejoin behavior. - PlayFab API behavior for extension, multiple BanIds and revoke route; current Worker ban records require migration/reconciliation. - Meta proof eligibility and verified legacy account mapping for BadGuyScene lookup when login is rejected. - Cloudflare plan, existing traffic, D1 transaction/quota headroom, backup and fail-closed availability policy. €0/100-CCU capacity is not confirmed. - Existing PUN session disconnection cannot be promised through ordinary Worker or PlayFab ban API. References: V22 `research/VOIDERATION_FINAL_ARCHITECTURE_V22_2026-10-30.txt`; V23 `research/VOIDERATION_DASHBOARD_DESIGN_V23.txt`; V24 `research/VOIDERATION_PLAYFAB_BANS_V24.txt`; V19 Worker/heartbeat report; local `worker.js`, `Oculus_Login.cs`, `PhotonVRManager.cs`. Official API references: PlayFab Admin Ban Users, Get User Bans, Revoke Bans; Photon PUN PlayFab and Custom Authentication documentation. Research only; no provider settings verified. END OF REPORT