1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
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
|