MVP Specification — Version 1
Overview
Section titled “Overview”This document defines the Minimum Viable Product (MVP) for the platform. The goal of this version is to validate the core data model, access control system, and collaborative match structure.
The system revolves around a few core domain objects: User, CanonicalPlayer, GhostPlayer, Match, and Community.
The MVP focuses on: identity → match recording → controlled access sharing
Statistics (derived, computed insight from match history) are explicitly deferred to Phase 2, alongside Data Portability — see Out of Scope.
1. Authentication & Player Model
Section titled “1. Authentication & Player Model”Functional Scope
Section titled “Functional Scope”- Users authenticate exclusively via Keycloak. Stratastax never stores or verifies a password itself — Keycloak owns authentication entirely, and may itself broker other providers (Google, Meta, …) behind a single realm.
- Upon first authentication with a given identity:
- UserProvisioningService provisions a User, together with exactly one CanonicalPlayer
- CanonicalPlayer becomes the permanent identity anchor for all future gaming history; User remains responsible for authentication only
Technical Scope
Section titled “Technical Scope”UserIdentityprovider(keycloak, or a provider Keycloak itself brokers)providerUserId(the JWTsubclaim)email,emailVerified(from the JWT, cached for display/lookup — not authoritative; Keycloak remains the source of truth)userId(belongs to User — never to CanonicalPlayer directly)User- immutable ID
- exactly one linked
canonicalPlayerId, created at the same time lastLoginAtCanonicalPlayer- immutable ID
- profile metadata (minimal MVP fields)
2. Match Creation & Structure
Section titled “2. Match Creation & Structure”Functional Scope
Section titled “Functional Scope”A user can create a Match with:
- Selected Game
- Participants (one or more)
- Optional Playset configuration (expansions, variants, scenario — shared across all participants)
Each participant is represented as a MatchParticipant, referencing a GhostPlayer and, where the Game defines it, a Loadout (faction, character, starting cards — specific to that one participant).
A MatchParticipant never stores a direct link to a CanonicalPlayer. Whether a participant’s GhostPlayer is claimed is resolved by looking up an active ClaimEdge, not by a field on MatchParticipant itself.
Technical Scope
Section titled “Technical Scope”gameIdplaysetId(optional)communityId(optional)recorderUserId— the Match’s owner; fixed, never transferred- timestamp
editRequestsEnabled(boolean, default true — see §3)lockedAt(null until the edit window elapses)
MatchParticipant
Section titled “MatchParticipant”ghostPlayerId- result fields (score, rank, outcome — per the Game’s ScoreType/ WinCondition)
disputeFlag(optional, settable only by the User owning the CanonicalPlayer that claims this participant’s GhostPlayer — see §3)
Loadout
Section titled “Loadout”- belongs to exactly one MatchParticipant
- faction / character / starting cards, where the Game defines them
Playset
Section titled “Playset”gameId(fixed at creation)expansionIds(0..N, must belong to the same Game)- variants / custom rules (free-form labels)
- scenario (optional)
ownerUserId- reusable across any number of Matches; content immutable once created, only its Name can be changed
Game / Expansion
Section titled “Game / Expansion”name,description,minPlayers,maxPlayers,imageUrl,tagsscoreType(Score|Time|Turns|WinLoss)winCondition(HigherWins|LowerWins; required unlessWinLoss)- Expansion belongs to exactly one Game
3. Match Editing & Trust System
Section titled “3. Match Editing & Trust System”Functional Scope
Section titled “Functional Scope”- Matches can be corrected after creation via a MatchEdit: score updates, participant corrections, or a change to which Playset a Match references.
- The Match’s owner is the sole approver of any MatchEdit.
- A participant may propose a MatchEdit only if the Match owner has
enabled edit requests for that Match (
editRequestsEnabled). - Edits are auto-approved if the proposer is a TrustedEditor for the Match’s owner.
- A Match becomes Locked once its configured edit window elapses; no further MatchEdits are possible after that, regardless of approval or trust.
- A participant may mark their own result as Disputed, with a reason — a visible annotation only, with no further workflow.
Technical Scope
Section titled “Technical Scope”MatchEditmatchId- changeSet (field-based: targeted MatchParticipant/Loadout fields, or a
new
playsetId) proposedByUserIdapprovedByUserId(the Match’s owner)- status:
pending | approved | rejected TrustedEditorgrantorUserId(a Match owner)trusteeUserId- directional; grantor can revoke at any time
MatchParticipant.disputeFlagreason(free text)- settable/clearable only by the User owning the CanonicalPlayer that claims the participant’s GhostPlayer
4. Ghost Player Claiming
Section titled “4. Ghost Player Claiming”Functional Scope
Section titled “Functional Scope”- Users can claim a GhostPlayer if they are the real participant it refers to.
- A claim must be approved by the GhostPlayer’s owner (the User who created it) before it takes effect — unless pre-approved via a trusted invitation path.
Technical Scope
Section titled “Technical Scope”ClaimRequestcanonicalPlayerId(the claimant)ghostPlayerId(one or more targets)validatedByUserId(the GhostPlayer’s owner)- status:
pending | approved | rejected ClaimEdgecanonicalPlayerIdghostPlayerId- revocable, auditable
Flow:
- User submits a ClaimRequest for one or more GhostPlayers.
- The GhostPlayer’s owner approves or rejects it (or it is pre-approved via a trusted invitation path).
- On approval, a ClaimEdge is created. The GhostPlayer is unchanged; the CanonicalPlayer gains the relationship.
5. Privacy System (Core MVP Feature)
Section titled “5. Privacy System (Core MVP Feature)”Functional Scope
Section titled “Functional Scope”Each data type supports a Privacy Level:
PrivatePrivilegedAccess(Group-based)Public
Applies, for the MVP, to:
ProfileMatchHistoryMatchDetails
Statistics is deferred to Phase 2, alongside the rest of the derived
statistics layer (see Out of Scope) — there is nothing to apply a
Privacy Level to yet, since no statistics are computed in this version.
Technical Scope
Section titled “Technical Scope”PrivacySettinguserIddataType(Profile|MatchHistory|MatchDetails)level(Private|PrivilegedAccess|Public)groupIds(0..N, only meaningful when level isPrivilegedAccess; every referenced Group must be owned by the same User)- absence of a row for a given
dataTypedefaults toPrivate - Access resolution logic:
- resolve the data type being requested;
- if
Public, allow; - if
Private, allow only the owning User; - if
PrivilegedAccess, allow the owning User and any User who is a member of one of the referenced Groups.
6. Groups (Access Control Layer)
Section titled “6. Groups (Access Control Layer)”Functional Scope
Section titled “Functional Scope”- Users can create Groups.
- Groups define who can access privileged data via a PrivacySetting.
- Group membership is managed unilaterally by the Group’s owner — adding someone to a Group never requires their approval, the same way keeping someone in a personal address book does not. There is no request/ approval flow for Group membership, in the MVP or beyond; that flow belongs to Community membership instead (see §8).
Technical Scope
Section titled “Technical Scope”GroupnameownerUserId— fixed, never transferredmemberUserIds
7. Community Layer
Section titled “7. Community Layer”Functional Scope
Section titled “Functional Scope”A Community is a higher-level shared space:
- Has its own Name, Description, and membership — not built from a Group’s member list
- May optionally delegate admin rights to a Group’s members (its AdminGroup), which must be owned by the Community’s current owner
- Contains shared match history context
- No tournament mechanics
Explicitly NOT included:
Section titled “Explicitly NOT included:”- time-based seasons
- competitive structures
- ranking systems
Technical Scope
Section titled “Technical Scope”Communityname,descriptionownerUserId— the only ownership in this model that can be transferred; transferring it clearsadminGroupIdadminGroupId(optional; must reference a Group owned by the currentownerUserId)joinPolicy(Open|RequiresApproval)- Rights model:
- Owner: everything, including transferring ownership, changing
adminGroupId, changingjoinPolicy, deleting the Community - Admin (owner, or any member of the AdminGroup): approve/reject MembershipRequests, edit Name/Description
- Member: no administrative rights
8. Community Membership
Section titled “8. Community Membership”Functional Scope
Section titled “Functional Scope”- Users can join a Community.
- Two modes, controlled by the Community’s
joinPolicy: Open— a Membership is created directly, no approval neededRequiresApproval— a MembershipRequest must be approved by the Community’s owner or an admin first
Technical Scope
Section titled “Technical Scope”MembershipuserIdcommunityId- at most one per (
userId,communityId) pair MembershipRequestproposedByUserIdcommunityIddecidedByUserId(owner or admin)- status:
pending | approved | rejected - only created when the target Community’s
joinPolicyisRequiresApproval
OUT OF SCOPE (Explicit Backlog)
Section titled “OUT OF SCOPE (Explicit Backlog)”The following are intentionally excluded from MVP:
1. Tournament System
Section titled “1. Tournament System”- No time-based competitive structure
- No seasons or ladders
2. Statistics & Statistic Snapshots
Section titled “2. Statistics & Statistic Snapshots”- No derived statistics computation of any kind, including the
StatisticsPrivacy data type - No scoped, point-in-time snapshots, and no active sharing layer (links, notifications) — targeted for Phase 2
3. Player Merging
Section titled “3. Player Merging”- No canonical identity deduplication system
4. Data Portability
Section titled “4. Data Portability”- No import/export APIs
- No external integrations
- Targeted for Phase 2, alongside Statistics
5. InviteOnly Community Join Policy
Section titled “5. InviteOnly Community Join Policy”- Only
OpenandRequiresApprovalare supported; invitation-based joining would need its own Invitation concept
Core MVP Outcome
Section titled “Core MVP Outcome”After implementation, the system must enable:
- A user registers → gets a User and exactly one CanonicalPlayer
- User records Matches with structured MatchParticipants, Loadouts, and an optional Playset
- Matches can be corrected safely, with the owner as sole approver and TrustedEditor for auto-approval, until the Match locks
- GhostPlayers can be claimed via ClaimRequest, resolved into a ClaimEdge
- Data visibility (Profile, MatchHistory, MatchDetails) is controlled via PrivacySettings
- Groups define privileged access boundaries, managed unilaterally by their owner
- Communities hold their own membership and shared history, optionally delegating admin rights via a Group
- Membership controls participation in Communities, either open or approval-based
Summary
Section titled “Summary”This MVP is intentionally focused:
Identity → Match data → Controlled access → Shared communities
Statistics and Data Portability are deferred to Phase 2. Everything else listed under Out of Scope is deferred further still.