BaaS Module Technical Specifications

Glicko-2 matchmaking with MMR + Rating Deviation, double-entry virtual currency ledger, sandboxed JS / TS cloud functions via a Node vm sandbox, rule-based anti-cheat (5 rule types), battle passes, tournaments, guild wars, push notifications across APNs / FCM / Web Push, WebRTC voice via SFU adapters with spatial 3D audio, in-game inbox with transactional reward delivery, achievements, challenges, leaderboards, LiveOps, marketplace, GDPR / COPPA / CCPA player-data export and deletion.

System Metrics

Data model

70 collections across every domain, all tenant-isolated and queryable via the standard Aeion API

Rating algorithm

Glicko-2 (MMR + RD)

Currency model

Double-entry ledger (atomic debit + credit)

Function runtime

Node `vm` sandbox (JS/TS transpiled); QuickJS WASM is roadmapped

Function memory limit

Configurable (default 128MB)

Matchmaking

MMR + RD widening (4 steps, configurable)

Push platforms

APNs (iOS), FCM (Android), Web Push

Loot roll

Seeded (deterministic) or probabilistic

Architecture Overview

BaaS spans capabilities across every domain of a game backend:

Capability inventory ├── Push notifications — APNs/FCM/WebPush, campaigns, device tokens ├── Inventory — Item holdings, bundles, trades ├── Inbox — In-game messages with attached rewards ├── Battle pass — Tiered progression, XP grants, reward tiers ├── Tournaments — Bracket/round-robin, prizes, team/solo ├── Anti-cheat — Rule engine, severity, auto-ban ├── Matchmaking — MMR+RD queue, widening, parties ├── Function executor — Sandboxed JS/TS VM execution ├── Leaderboards — Score submission, anti-cheat validation, resets ├── Guild wars — Guild vs guild competition ├── Match lifecycle — Match creation, events, disputes ├── Remote config — Feature flags, A/B test config ├── Challenges — Objectives, progress tracking, rewards ├── Currency ledger — Double-entry ledger, atomic transactions ├── Match chat — Per-match chat, mutes, moderation ├── Live ops — Events, seasons, participation tracking ├── Marketplace — Player-to-player item trading ├── Function deployment — Code deployment, versioning, analysis ├── Alerting — Real-time metric alerting ├── Guilds — Guild lifecycle, ranks, applications ├── Match rooms — Room management, player slots ├── Chat channels — Text channel subscriptions ├── Presence — Online/offline, last seen ├── Retention — Cohort tracking, churn signals ├── Loot — Drop rate resolution, seed-based rolls ├── Rating engine — Glicko-2 implementation ├── Trading — Atomic two-party trades ├── Voice — Voice room management ├── Bans — Account suspension, ban appeals ├── Match room broadcast — Real-time room event broadcast ├── Matchmaking tickets — Match seat reservations ├── Reward retries — Retry queue for failed rewards ├── Usage metering — Billing usage metering ├── Webhook delivery — Outbound webhook retry logic ├── Function triggers — Live log streaming, event triggers ├── Realtime broadcast — WebSocket broadcast hub ├── Usage rollups — Hourly → daily → monthly rollup ├── Achievements — Achievement definitions + progress └── Async job queue — Background job processing for async tasks

Data model (50+ tables): The full schema spans every domain above — projects, players, currencies and balances, inventory and trades, matches and disputes, guilds and guild wars, live events, inbox messages, remote config, chat and presence, anti-cheat rules and flags, bans, matchmaking tickets, player ratings, functions and executions, and usage aggregates — all tenant-isolated and queryable via the standard Aeion API.

Rating Engine — Glicko-2 Implementation

Glicko-2 is a significant upgrade over ELO. It tracks:

  • Rating (r): True skill estimate (starts at 1500 for new players)
  • Rating Deviation (RD): Confidence in the rating (starts at 350, decays for inactive players)
  • Volatility (σ): How erratic the player's performance is

Each player's rating record:

json { "rating": 1500, "rd": 350, "volatility": 0.06, "gamesPlayed": 0, "isRanked": false, "lastGameAt": "2026-06-01T00:00:00Z" }

RD decay: If a player is inactive, RD increases each rating period (typically weekly) using the published Glicko-2 decay formula, tuned by a configurable decay constant (default: 5-10). This means a player who hasn't played in 4 months has a much higher RD — their rating is less certain, and they should be matched more loosely.

After a match: Both players' rating, RD, and volatility update based on the outcome (win, loss, or draw) relative to the expected result.

Matchmaking integration: The matchmaking engine reads each player's RD alongside their rating. Two players with similar ratings but very different RDs are matched with different tolerances — a player with RD=350 (new) can be matched against RD=50 (veteran) as long as the MMR gap is within the widen threshold.

Matchmaking — MMR + RD Queue Model

Queue configuration:

json { "name": "string", "region": "string", "gameMode": "string", "minPlayers": 2, "maxPlayers": 10, "teamSize": 1, "skillDeltaSteps": [50, 100, 200, 400], "widenTimesSeconds": [0, 30, 60, 120], "antiSmurfGames": 10, "defaultStartingMmr": 1500, "defaultStartingRd": 350, "ticketHeartbeatSeconds": 5, "ticketMaxLifetimeSeconds": 300, "priorityBias": "fifo | skill | fast" }

Each queue ticket tracks the player, an optional party ID (for squad matchmaking), current MMR and RD, ticket state (waiting / matched / cancelled / expired), and heartbeat/match timestamps.

Match draft (the core matching algorithm): when the matchmaker forms a match, it groups tickets into teams and records the average MMR and MMR spread (max − min) across the resulting match, alongside the queue and region.

Widen strategy: skillDeltaSteps (4 values) and widenTimesSeconds (3 values) are deliberately asymmetric — the queue starts at the first skill-delta step immediately, then widens to each subsequent step as its corresponding time threshold passes. After the last configured time threshold, the spread caps at the last configured step.

Anti-smurf enforcement: players who haven't completed their configured placement-game count are treated as unranked and matched in a separate provisional pool until placement completes.

Leaderboards — Score Submission & Validation

Leaderboard types: global, weekly, tournament, speed-run, or custom. Scoring direction: ascending or descending (descending — higher is better — is standard). Reset period: none, daily, weekly, or monthly.

Submitting a score:

json { "leaderboardId": "string", "userId": "string", "score": 0, "displayName": "string", "avatarUrl": "string", "metadata": {}, "submittedAt": "2026-06-01T00:00:00Z" }

Response:

json { "entry": { "...": "ranked entry" }, "isNewRecord": true, "previousScore": null, "rankChange": 0 }

Anti-cheat integration: before ranking, every submitted score is validated against the anti-cheat engine — checked against the player's personal best and the leaderboard's global mean. If the check fails, the entry is flagged with a reason and marked unvalidated rather than silently rejected.

Score submission rules:

  • onlyBetterScores: If true, a submitted score only replaces the previous score if it's better in the scoring direction
  • allowUpdates: If false, scores are immutable once submitted (no resubmission)
  • maxEntries: Maximum number of entries stored per player per leaderboard

Reset handling: resetPeriod determines when entries are cleared. lastResetAt tracks the last reset timestamp. Weekly resets clear all entries and start fresh — useful for competitive seasons.

Currency — Double-Entry Ledger

Every currency operation writes two linked transaction rows — one debit, one credit — that always net to zero:

json { "userId": "string", "currency": "soft | hard | premium", "delta": 0, "reason": "match_reward | achievement_unlock | battle_pass_tier | challenge_complete | purchase | refund | trade | admin_grant | admin_revoke | expiration | system", "sourceId": "matchId, achievementId, etc.", "balanceAfter": 0, "pairedTransactionId": "string", "createdAt": "2026-06-01T00:00:00Z" }

delta is signed (negative for the debit side, positive for the credit side); pairedTransactionId links the two rows of the same transfer back to each other.

Atomic operations: debiting an account fails outright if the balance is insufficient; crediting an account always succeeds; transferring between two accounts debits one and credits the other in a single all-or-nothing operation. Every operation runs inside a database transaction, so a balance can never go negative from a partial write.

Balance tracking: the current balance per account and currency is tracked directly, but transactions are append-only — balance is always re-derivable as SUM(credits) - SUM(debits) from the transaction log, not just a mutable field.

Loot — Drop Rate Resolution

A loot table entry:

json { "itemId": "string", "dropRate": 0.0, "minQuantity": 1, "maxQuantity": 1, "weight": 1, "conditions": [ { "field": "player.level", "operator": "gte | lte | equals | in", "value": 0 } ] }

Rolls can be seeded (deterministic — same seed always produces the same result, useful for fairness audits) or purely probabilistic.

Drop rate resolution: load the table's entries, filter by any player-state conditions (level, battle-pass tier, etc.), roll each eligible entry against its drop rate, and distribute the resulting items or currency to the player — with every roll logged for audit.

Use cases: Loot chests, achievement rewards, battle pass tier rewards, challenge completion bonuses, random daily login rewards, gacha pulls.

Anti-Cheat — Rule Engine

Rule types: rate-limit, impossibility, account-age, honeypot, custom. Applies to: all, score, achievement, purchase, currency, or loot. Severity: low, medium, high, critical.

Validation result: every check returns whether the caller should proceed, the action taken (allowed / flagged / blocked), a reason and severity if triggered, and which rules matched.

A rule definition carries its type, what it applies to, the action to take (flag or block), severity, rule-specific config, priority, an active flag, and whether critical hits should auto-ban.

Rule configurations by type:

```typescript // rate_limit { windowSeconds: 60, maxActions: 5, scope: "userId" | "ip" | "leaderboardId" }

// impossibility { theoreticalMax: 100000, stdDeviations: 5, checkAgainstGlobalMean: true }

// account_age { minimumDays: 30 }

// custom — JS expression evaluated in sandbox { expression: "input.value < player.lifetimeValue * 0.1" } ```

Severity → action mapping:

typescript low/medium/high: action: "flag" → status: "pending" → reviewed manually → resolved as dismissed / acknowledged / escalated critical: action: "block" → autoBanOnCritical: true → 24-hour auto-ban

Every flag starts pending regardless of severity — there's no automatic timer or hit-count that escalates it on its own. A human reviewer resolves each one to dismissed, acknowledged, or escalated.

Evidence collection: Every validation logs: player history, account age, recent sessions, peer performance comparison. Full context for human review.

Function Runtime — Sandboxed Execution

Trigger types: adhoc, http, cron, event. Execution status: running, success, failure, timeout, memory-exceeded, rejected.

Every execution record captures its status, duration, peak memory, output or captured error, the log lines produced, how many Aeion API calls it made, and a reject reason if it was blocked before running.

Execution flow:

  1. Load active version of the function (or specific versionId for ad-hoc test)
  2. Transpile TypeScript if runtime: "ts"
  3. Run static analysis — check for blocking issues
  4. Spin up a Node vm sandbox with:
  5. Execute the compiled function inside the sandbox
  6. Capture output, duration, memory peak
  7. Store execution record with logs

`aeion` API available in sandbox:

typescript aeion.db.find / findById(); // Database access — read-only aeion.currencies.add / deduct / balance(); // Currency operations aeion.inventory.addItem / consumeItem(); // Item management aeion.log.info / warn / error(message, meta); // Structured logging aeion.events.emit(name, payload); // Emit game event

`console.log` → live clients: The function trigger router captures sandbox logs and broadcasts them via WebSocket. Clients subscribe to live function output — streaming AI responses, real-time state updates, debugging.

Push Notifications — Multi-Platform Campaigns

Platforms: iOS, Android, web, desktop. Delivery status: queued, sent, delivered, opened, dismissed, failed, suppressed. Campaign status: draft, scheduled, processing, completed, cancelled, failed.

Each device token record tracks the player, device, platform, the underlying APNs/FCM token, whether it's active, last-active time, consecutive-failure count, and per-player notification preferences (marketing opt-in, system opt-in, quiet hours).

Campaign configuration:

json { "title": "string", "body": "string", "imageUrl": "string | null", "deepLink": "string | null", "variables": { "playerName": "resolved per recipient" }, "targetSource": "all devices | segment | specific users", "mirrorToInbox": true, "attachedRewards": [{ "type": "currency | item | xp" }], "priority": "low | normal | high | critical", "respectQuietHours": true, "expiresAfterHours": null }

Attached rewards: when a user opens a push notification, the attached rewards (currency, items, XP) are delivered transactionally. If reward delivery fails, the message is marked failed and stays retryable.

Mirror to inbox: mirrorToInbox: true creates a corresponding in-game inbox message alongside every push delivery. Useful for players who have push disabled — they see the message in their in-game inbox instead.

Battle Pass — Tiered Progression System

Each tier defines the cumulative XP required to reach it, plus its free-track and premium-track rewards (currency, item, or bonus XP).

Player progress tracks total XP, current tier, whether the premium track is unlocked, which free/premium tiers have been claimed, and a full XP history (amount, source, and originating event for every grant).

XP sources are configured per battle pass: XP per challenge completed, XP per achievement unlocked, XP per leaderboard score submitted.

Tier advancement: granting XP appends to the player's history, recalculates their current tier against the cumulative thresholds, auto-claims rewards where configured, triggers the underlying currency/item grants, and emits a tier-progress event.

Tournaments — Bracket & Prize Management

Tournament types:

  • Bracket — Single elimination, double elimination, triple elimination
  • Round Robin — Every team plays every other team
  • Swiss — Used in chess/esports for large fields without full round robin

Match generation: loads the registered participants and bracket structure, generates every match for each round or heat, and orders them appropriately for the format (bracket position for elimination brackets, round-robin table for round robin).

Prize distribution: loads the tournament's configured prize structure, determines final rankings, and for each prize tier identifies qualifying participants, grants the associated currency/items, and records the prize in each participant's history.

LiveOps events:

  • Daily login bonuses
  • Weekly challenges
  • Seasonal content rotations
  • Time-limited store offerings
  • Participation leaderboards for events

Key Architecture Decisions

Rating system

Glicko-2 (MMR + RD) — Tracks skill uncertainty, not just wins/losses

Matchmaking

MMR + RD widening — Ensures fair games even for inactive players

Currency

Double-entry ledger — Mathematically impossible to dupe — always balanced

Inventory

Source tracking — Know where every item came from (purchase/granted/trade/found)

Anti-cheat

Rule engine — Configurable per project, not a static blacklist

Function sandbox

Node `vm` — Isolated execution, memory/time limits enforced (QuickJS WASM roadmapped)

Function logs

WebSocket broadcast — Live function output to game clients

Loot

Seeded + probabilistic — Seeded for reproducible results (gacha fairness)

Push

APNs + FCM + Web — All three platforms from one API

Inbox rewards

Transactional — Reward failure → retry, never lost

Battle pass XP

Source-tracked history — Full audit trail of every XP grant

Game Backend Infrastructure