Disciplines · Audits

V-Series Platform Consolidation Plan — Shared Online Services & Cross-Game Identity

Every game version specs its own auth/matchmaking/leaderboards/anti-cheat/ telemetry layer, in a different language and framework:

2sections7 minread

On this page

Created: 2026-06-12 (per V1_V7_PLAN_SET_AUDIT_2026-06-12.md §6.1 items 2–3) Status: Proposed Companion: V_SERIES.md (portfolio map)

Problem 1 — Five parallel online-services stacks#

Every game version specs its own auth/matchmaking/leaderboards/anti-cheat/ telemetry layer, in a different language and framework:

Ver Stack Count Where
V2 TypeScript Nx adapter libs 50 V2/services/
V4 Rust + Axum microservices 25 specced (13 scaffolded) V4/services/
V5 NestJS services over one shared runtime 16 V5/services/
V6 Rust services (world/gateway/Ori) 9 apps/v6/
V7 Rust/TS services 8 apps/v7/

All five doc sets simultaneously preach "layer on V1, don't fork." The duplicated surface — session auth, parties/friends, matchmaking, leaderboards, telemetry ingest, anti-cheat signal collection, replay storage — is the single most repeated engineering plan in the portfolio, while the genuinely game-specific parts (V2 rollback transport, V4 lockstep, V6 agent cognition, V7 realm meshing) are not shareable and rightly live per-version.

Decision#

Extract a shared game-platform service tier ("GPS") with V7's hybrid trust model as the design anchor (platform-inviolable identity/economy/safety; game-specific logic stays per-title):

  • GPS-owned (one implementation, multi-tenant by gameId): player session auth (on V1 identity), friends/parties/presence, matchmaking orchestration (per-game rule plugins), leaderboards/seasons, telemetry ingest + schema registry, anti-cheat signal bus + case management (per-game detectors plug in), replay blob storage + metadata, entitlements (see Problem 2), and creator-payout rails (Abundantia generalized).
  • Per-game (never GPS): netcode/simulation, game servers, world/realm state (Nephthys stays V7's), gameplay services (V6 Ori cognition tiers), and any latency-critical in-match path.
  • Language ruling: GPS services follow the repo's stack policy — Rust for the hot paths (telemetry ingest, presence fan-out), TypeScript/Nx for control-plane CRUD. V5's NestJS tier and V2's adapter libs migrate by consumption, not rewrite: new GPS endpoints are stood up first, then each game's existing service spec is re-pointed and its local implementation retired section by section.

Sequencing (relative milestones, no calendar commitment)#

  1. M1 — contracts: define libs/contracts/gps/ (OpenAPI + event schemas) for the nine GPS domains above; each game version maps its existing spec to the contract and records deltas as ADRs in its own docs/decisions/.
  2. M2 — first consumer: V7 (earliest-stage implementation, strongest trust-model fit) builds against GPS natively instead of its own §-tier service stubs; V6's gateway adopts GPS presence/telemetry.
  3. M3 — brownfield adoption: V5 re-points its 14 wrapper services (only anti-cheat + compliance-dsar hold real logic today — those migrate as GPS plugins); V4's 12 /health-only routers are implemented once, as GPS, not re-scaffolded per game; V2's adapter libs consume GPS.
  4. Exit criterion: no new per-game implementation of any GPS-owned domain is accepted; per-game TODOS sections for those domains point at GPS contract versions.

Risks#

  • Multi-tenant blast radius (one GPS outage hits every title) → per-game cell isolation + per-tenant rate isolation are M1 contract requirements.
  • Premature abstraction (only V3 is near-operational today) → GPS extracts only domains already specced ≥3 times (the table above), nothing speculative.

Problem 2 — No cross-game account/entitlement story#

V4 claims Iris-based identity reuse; V6's Ori carries agents across realms; V7 extends the Ori to carry player characters; V2 specs cross-product entitlements ("Lilith meditation-streak unlocks a V2 card border"). No document defines the player's single account, wallet, and entitlement graph across V2–V7.

Decision#

One Oshun player account (V1 identity, already the auth root everywhere) with three attached graphs:

  1. Profile graph — display identity, age/region attributes (single source for every game's age gate), platform links (PSN/Xbox/Steam/Apple/Google), block/mute lists shared portfolio-wide (a block in one title blocks in all — safety floor, per V7's platform-inviolable rule).
  2. Entitlement graph — purchases, unlocks, cross-product grants. Schema: entitlement(id, ownerAccount, grantingProduct, consumingProduct, kind, sourceRef, platformConstraints, revocation). Platform-store constraints (an iOS-purchased item's visibility on PSN, cert-ban interactions per V2/legal/platform-cert-bans.json) are first-class fields, not afterthoughts. Wallet: Aje rails for crypto where certified; platform stores elsewhere; one ledger regardless of rail.
  3. Character/being graph — the Ori event store, with V6 agents and V7 player characters as distinct subject types (schema-evolution rules in V6/docs/ori-schema-evolution.md). Game saves stay per-title; only portable identity state crosses titles.

Rules#

  • A game may read cross-game attributes only through scoped, consented queries (V1 Iris consent model governs); no bulk profile export to game services.
  • Cross-product grants (the V2↔Lilith example) are declared in the granting product's catalog and validated against the consuming product's cert-ban registry at publish time, not at runtime.
  • Deletion: account erasure fans out to every attached graph with per-product attestation (extends V1's DSAR pipeline; V3 already routes through it — the same cascade covers entitlements and Ori subjects, with V7 realm-side deletion handled per V7/docs/operator-data-protection.md).

Open items (tracked, not silently deferred)#

Rewritten on 2026-09-18: the first and third lines were statements, not tasks. The contracts library keeps each domain under libs/contracts/src/<domain>/, so the GPS contracts go to libs/contracts/src/gps/, not the libs/contracts/gps/ path M1 names.

  • GPS.M1 Start milestone M1: create libs/contracts/src/gps/ with zod schemas and the OpenAPI document for the nine GPS-owned domains listed under "Decision", exported as @oshun/contracts/gps, each schema carrying gameId as the tenant key and the per-game cell and rate-isolation fields the "Risks" section requires. One child per domain so each can land alone.
    • GPS.M1.a Player session auth on V1 identity (session, platform links, portfolio-wide block and mute lists). Verify: gps/session.spec.ts round-trips valid and invalid fixtures; a block recorded for one gameId is visible to a query for another.
    • GPS.M1.b Friends, parties and presence. Verify: gps/social.spec.ts: a friend edge is symmetric and scoped to the account, not the gameId; a party carries one gameId and refuses a member blocked by any other member; a presence record without an expiry is refused.
    • GPS.M1.c Matchmaking orchestration with a per-game rule-plugin contract. Verify: spec with two plugins that disagree.
    • GPS.M1.d Leaderboards and seasons. Verify: gps/leaderboards.spec.ts: a score is keyed by gameId, board, season and account; a submission to a closed season is refused; two games' boards with the same name do not collide.
    • GPS.M1.e Telemetry ingest and its schema registry. Verify: spec that an unregistered event type is refused.
    • GPS.M1.f Anti-cheat signal bus and case management, with the per-game detector plug-in contract. Verify: gps/anticheat.spec.ts: a signal names its detector, its version and its gameId; a case cannot close without an actor and a reason; a detector plug-in that declares no signal types is refused.
    • GPS.M1.g Replay blob metadata. Verify: gps/replays.spec.ts: a record carries the content hash, the byte length, the game build it was recorded on and its retention class; a record with no hash is refused.
    • GPS.M1.h Entitlements: entitlement(id, ownerAccount, grantingProduct, consumingProduct, kind, sourceRef, platformConstraints, revocation) with platform-store constraints as first-class fields, validated against V2/legal/platform-cert-bans.json at publish time. Verify: spec that a grant violating a cert ban is refused when it is declared, not at runtime.
    • GPS.M1.i Creator-payout rails generalised from Abundantia. Verify: gps/payouts.spec.ts: amounts are integer minor units with a currency; a payout names its rail, its run and the ledger entries it settles; the creator floor Abundantia enforces (70%) is a field the schema requires, not a default.
    • GPS.M1.j Each game version maps its existing service spec to these contracts and records the deltas as ADRs in its own docs/decisions/ (V2, V4, V5, V6, V7). Verify: one ADR per version; no domain left unmapped.
  • GPS.CERT Review the entitlement schema against each platform's cross-buy policy before any cross-product grant ships. Board tag 2026-09-18: a review of store policies by a person with access to the partner agreements; an agent prepares the policy-by-field table from the public documents. blocked:governance
  • GPS.ORI Add the Ori subject-type discriminator (V6 agent versus V7 player character) to V6's event schema under the evolution rules of V6/docs/ori-schema-evolution.md, before V7 writes player characters into the store. Verify: a schema-evolution spec reads events written before the change, and an event without a subject type is refused after it.