The Squatchin' backend is the authoritative control plane for persistent player identity, venue authorization, game creation, role assignment, location-package generation, moderation, telemetry intake, final results, and audit records.
The backend is deliberately not the real-time gameplay engine. Once an active player has downloaded and validated a signed game bootstrap package, routine gameplay must continue without a server connection.
The backend shall not participate in ordinary:
Those functions are handled by the mobile application and nearby-device protocol.
A player must be able to complete normal rounds after the game starts even when mobile data or Wi-Fi becomes unavailable.
The server is required before and after active gameplay for:
The server is authoritative for:
The client is authoritative for ordinary local gameplay execution after the signed bootstrap package is accepted.
The backend shall minimize collection and retention of personally identifying information, raw location information, and nearby-player interaction history.
The server shall not store, by default:
| Service | Responsibilities |
|---|---|
| Authentication and Game Identity Service | Authenticates users, issues sessions, maintains persistent player identity between games. |
| Gameplay Profile Service | Stores gameplay-relevant appearance traits and photo-verification status. |
| Green-Light Service | Verifies venue authorization and issues one-time game-creation grants. |
| Game Orchestration Service | Creates games, manages lobbies, controls game state, locks membership, and assigns roles. |
| Location Package Service | Builds, signs, versions, and distributes player-specific offline game packages. |
| Telemetry and Directive Service | Accepts operational telemetry batches and returns signed server directives. |
| Moderation Service | Removes players, pauses games, resumes games, terminates games, and records action history. |
| Results and Audit Service | Persists final outcomes, event-ledger digests, game summaries, and anomaly flags. |
A first implementation may use a modular monolith with clear service boundaries. The interfaces defined here should allow later separation into independently deployable services without changing the mobile API.
| Object | Description |
|---|---|
| User | Persistent authenticated person or account. |
| Game Profile | Gameplay-relevant traits, verification status, and eligibility state. |
| Device Installation | A specific app installation and its capabilities. |
| Green Light | Signed authorization allowing a game to be created at a specific approved venue or game zone. |
| Venue | Approved physical establishment, event site, or organizer-controlled location. |
| Game Zone | Approved playable area within a venue or event footprint. |
| Location Node | A specific valid round destination inside a game zone. |
| Game | A single Squatchin' session with configuration and lifecycle state. |
| Game Member | A game-scoped pseudonymous representation of a player. |
| Role Assignment | Private server-assigned role and role certificate. |
| Bootstrap Package | Signed offline game package delivered to one game member. |
| Telemetry Event | Operational app-health or status event. |
| Moderation Action | Server-side record of an authorized moderation decision. |
| Moderation Directive | Signed command delivered to a player app. |
| Game Result | Final player-level and game-level outcome. |
| Audit Record | Event digest, anomaly flag, or review record associated with a game. |
| State | Description |
|---|---|
draft |
Created but not yet open for players. |
lobby |
Players may join or leave. |
starting |
Membership is locked; roles and packages are being generated. |
active |
Local gameplay is underway. |
paused |
Active game temporarily halted by a moderator. |
completing |
Local final rounds have ended; result synchronization is underway. |
completed |
Game has concluded and final results are available. |
cancelled |
Creator cancelled before active play began. |
terminated |
Moderator or administrator ended the game early. |
| State | Description |
|---|---|
joined |
Player is in the lobby. |
active |
Player may participate in local gameplay. |
spectator |
Player can observe permitted end-state information but cannot submit gameplay actions. |
boot_pending |
Server issued a removal directive awaiting client acknowledgement. |
booted |
Player is removed from active gameplay. |
left |
Player voluntarily left before game start. |
disconnected |
Informational state based on missing telemetry; not an automatic elimination state. |
All public endpoints shall:
/v1 route prefix.Idempotency-Key header on retryable write requests.request_id in responses and errors.{
"error": {
"code": "GAME_NOT_JOINABLE",
"message": "This game is no longer accepting players.",
"request_id": "uuid"
}
}
| HTTP Status | Meaning |
|---|---|
400 |
Malformed request |
401 |
Authentication missing, expired, or invalid |
403 |
Authenticated but lacks required permission |
404 |
Requested game, venue, package, or member not found |
409 |
State conflict, duplicate join, or invalid transition |
410 |
Expired green light, join code, or package |
422 |
Valid request structure but invalid game rule |
429 |
Rate limit reached |
503 |
Temporarily unavailable |
Authentication, profile data, settings, and role-based access control are in scope only where they carry information into the game.
| Role | Scope |
|---|---|
| Player | May join eligible games and participate in local gameplay. |
| Game Creator | May create and configure an authorized game within permitted limits. |
| Moderator | May pause, resume, remove members, and terminate assigned games. |
| Administrator | May manage venue authorization, review audit records, and perform elevated moderation. |
| Method | Route | Purpose | Required Role |
|---|---|---|---|
POST |
/v1/auth/session |
Create authenticated session | None |
POST |
/v1/auth/session/refresh |
Refresh access token | Authenticated session |
DELETE |
/v1/auth/session |
Log out current device | Authenticated user |
GET |
/v1/me/game-identity |
Retrieve game eligibility and persistent identity state | Authenticated user |
PUT |
/v1/me/game-profile |
Update gameplay traits | Authenticated user |
POST |
/v1/me/game-profile/photo-verification |
Upload verification photo | Authenticated user |
PUT |
/v1/me/game-device |
Register app installation capabilities | Authenticated user |
{
"user_id": "uuid",
"game_identity_status": "eligible",
"photo_verification_status": "approved",
"capabilities": {
"may_create_game": true,
"may_moderate_game": false
}
}
| Method | Route | Purpose | Required Role |
|---|---|---|---|
POST |
/v1/green-lights/validate |
Validate scanned or entered green light | Eligible player |
GET |
/v1/green-lights/{green_light_id} |
Retrieve limited approved metadata | Eligible player |
POST |
/v1/admin/green-lights |
Issue a green light | Administrator or authorized venue operator |
POST |
/v1/admin/green-lights/{green_light_id}/revoke |
Revoke a green light | Administrator or authorized venue operator |
{
"green_light_token": "signed-location-token",
"requested_game_template": "standard_social_deduction",
"device_time": "2026-06-20T19:00:00Z"
}
{
"creation_grant": "one-time-opaque-grant",
"venue_id": "uuid",
"game_zone_id": "uuid",
"expires_at": "2026-06-20T20:30:00Z",
"max_players": 40,
"max_rounds": 8,
"permitted_templates": [
"standard_social_deduction"
]
}
| Method | Route | Purpose | Required Role |
|---|---|---|---|
GET |
/v1/games?state=joinable |
List joinable public games | Eligible player |
POST |
/v1/games |
Create a draft game | Eligible player with creation grant |
GET |
/v1/games/{game_id} |
Retrieve authorized game summary | Member, creator, moderator |
PATCH |
/v1/games/{game_id} |
Update permitted configuration | Creator or moderator |
POST |
/v1/games/{game_id}/members |
Join game using join code | Eligible player |
DELETE |
/v1/games/{game_id}/members/me |
Leave before game start | Game member |
POST |
/v1/games/{game_id}/start |
Lock lobby and start game | Creator or moderator |
{
"creation_grant": "one-time-opaque-grant",
"name": "Saturday Night Squatchin'",
"visibility": "private",
"max_players": 24,
"round_count": 5,
"start_mode": "creator_start",
"game_template": "standard_social_deduction"
}
{
"game_id": "uuid",
"game_state": "lobby",
"join_code": "MOSS-ELK",
"join_code_expires_at": "2026-06-20T19:30:00Z",
"creator_game_member_id": "uuid"
}
| Method | Route | Purpose | Required Role |
|---|---|---|---|
GET |
/v1/games/{game_id}/bootstrap |
Download member-specific signed package | Active game member |
POST |
/v1/games/{game_id}/sync |
Recover from reconnect or restart | Game member |
GET |
/v1/games/{game_id}/membership-status |
Confirm active, spectator, or booted state | Game member |
POST |
/v1/games/{game_id}/boot-acknowledgements |
Confirm receipt of signed boot directive | Game member |
GET |
/v1/games/{game_id}/summary |
Retrieve completed game summary | Game member, moderator, administrator |
The bootstrap package must include:
The mobile app must verify the package signature before using it.
| Method | Route | Purpose | Required Role |
|---|---|---|---|
POST |
/v1/games/{game_id}/telemetry/batches |
Submit operational telemetry | Game member |
POST |
/v1/client-errors/batches |
Report application errors | Authenticated user |
Telemetry is used for technical operation and directive delivery. It is not used to adjudicate normal GPS validation, local guesses, or Bluetooth interactions.
| Method | Route | Purpose | Required Role |
|---|---|---|---|
POST |
/v1/games/{game_id}/results |
Submit final result and event-ledger digest | Game member |
GET |
/v1/games/{game_id}/summary |
Retrieve final results | Game member, moderator, administrator |
GET |
/v1/games/{game_id}/audit-summary |
Retrieve audit overview | Moderator, administrator |
| Method | Route | Purpose | Required Role |
|---|---|---|---|
POST |
/v1/games/{game_id}/moderation/remove-members |
Remove member from active game | Moderator, administrator |
POST |
/v1/games/{game_id}/moderation/pause |
Pause game | Moderator, administrator |
POST |
/v1/games/{game_id}/moderation/resume |
Resume game | Moderator, administrator |
POST |
/v1/games/{game_id}/moderation/terminate |
End game immediately | Moderator, administrator |
A green light is an approved authorization permitting game creation at a venue or event location.
It may originate from:
A green light does not itself create a game. It must be validated by the backend and exchanged for a short-lived, one-time creation grant.
| Field | Purpose |
|---|---|
green_light_id |
Unique authorization record |
venue_id |
Approved venue or organizer |
game_zone_id |
Permitted playable area |
issued_at |
Authorization issue time |
expires_at |
Authorization expiration time |
max_players |
Venue capacity limit |
max_rounds |
Venue rule limit |
allowed_templates |
Permitted game modes |
revocation_version |
Revocation validation data |
signature |
Cryptographic authorization proof |
The backend must reject a green light when:
A player may create a game when all conditions are true:
A creator may set, within server-approved bounds:
The backend shall control:
Before a game can move from lobby to starting, the backend must verify:
The backend must:
assignRoles(game_id):
members = loadEligibleGameMembers(game_id)
role_policy = loadRolePolicy(game_id)
validateMinimumPlayers(members, role_policy)
secure_seed = cryptographicRandom()
assignment_commitment = sha256(secure_seed)
shuffled_members = deterministicShuffle(members, secure_seed)
role_map = allocateRoles(shuffled_members, role_policy)
begin transaction
lockGameMembership(game_id)
storeAssignmentCommitment(game_id, assignment_commitment)
for each member in role_map:
certificate = signRoleCertificate(
game_id,
member.game_member_id,
member.role
)
saveRoleAssignment(member, certificate)
markGameActive(game_id)
commit transaction
The backend must generate and distribute enough information for a player to complete all normal rounds without requesting a location from the server each round.
The package may be:
The preferred initial implementation is a full, signed player-specific package downloaded at game start.
The backend must:
| Package Component | Visibility |
|---|---|
| Game rules | All game members |
| Venue safety rules | All game members |
| Server public key / trust anchor | All game members |
| Game-scoped member identity | One game member |
| Private role | One game member |
| Role certificate | One game member |
| Round route | One game member |
| Location GPS data | One game member |
| Entry challenge verifier | One game member |
| Directive verification data | One game member |
generateBootstrapPackages(game_id):
game = loadActiveGame(game_id)
members = loadActiveMembers(game_id)
locations = loadApprovedLocationNodes(game.game_zone_id)
for each member in members:
route = buildRoleAwareRoute(
role = member.role,
round_count = game.round_count,
approved_locations = locations,
movement_rules = game.rules
)
bundle = createSignedBootstrapBundle(
game = game,
game_member = member,
role_assignment = member.role_assignment,
route = route,
game_rules = game.rules
)
saveBootstrapPackage(member.game_member_id, bundle)
Telemetry exists to support:
Telemetry does not replace local gameplay rules.
By default, telemetry should contain:
Telemetry should not contain continuous raw GPS coordinates unless an explicit future safety or incident feature is approved with its own privacy policy.
Moderators may:
Administrators may additionally:
A Bluetooth message may never remove a player from a game.
Only a server-signed moderation directive may initiate client removal behavior.
A boot directive must include:
The server must record:
At the end of a game, each app submits a concise result record and local event-ledger digest.
The backend stores the outcome without attempting to reconstruct every Bluetooth interaction.
{
"game_id": "uuid",
"game_member_id": "uuid",
"final_state": "survived",
"rounds_completed": 5,
"correct_guesses": 4,
"incorrect_guesses": 2,
"local_event_ledger_root_hash": "hash",
"role_receipt_hashes": [
"hash-1",
"hash-2"
],
"submitted_at": "2026-06-20T20:10:00Z"
}
The backend shall:
| Function | Inputs | Output | Failure Conditions |
|---|---|---|---|
validateGreenLight() |
Token, requester, requested template | One-time creation grant | Invalid, expired, revoked, or unauthorized token |
createGame() |
Creation grant, permitted settings | Draft game and join code | Grant consumed, invalid settings, venue limits exceeded |
joinGame() |
Game ID, join code, user | Game membership | Lobby closed, code invalid, user ineligible |
leaveGame() |
Game member | Removed lobby membership | Game already active |
updateGameConfiguration() |
Creator request, game state | Updated approved config | Unauthorized field or state conflict |
startGame() |
Game ID, authorized user | Active game | Invalid members, green light expired, startup error |
assignRoles() |
Eligible game members, role policy | Private assignments | Insufficient players or transaction failure |
createRoleCertificate() |
Game member, role | Server-signed certificate | Signing-key failure |
assignLocations() |
Game, members, approved nodes | Member-specific routes | Insufficient valid location nodes |
createBootstrapPackage() |
Assignment, route, rules | Signed package | Package generation failure |
deliverBootstrapPackage() |
Game member | Downloadable package | Member not active |
receiveTelemetryBatch() |
Telemetry events | Receipt and directives | Invalid session, duplicate events, invalid schema |
issueModerationDirective() |
Authorized action | Signed directive | RBAC failure or invalid game state |
confirmMembershipStatus() |
Game member | Active, spectator, booted, or terminated state | Member not found |
recordResults() |
Result payload and audit digest | Persisted result | Invalid game state or duplicate conflict |
generateGameSummary() |
Game ID | Final summary | Game not complete |
reviewGameAudit() |
Game ID, authorized reviewer | Audit overview | RBAC failure |
terminateGame() |
Authorized request | Terminated game state | Unauthorized request |
The backend must:
The backend design is implementation-ready when the following are true: