AI Initiative logoAI Initiative
Reading room / Game Creation Studio

02. Architecture

Describes host, SDK, optional runtime kit and game boundaries, postMessage bridge, saves, identity, AI gate, rooms, code overrides and offline shell.

Engineering architecture reference
This is the retained source text, not a new validation of its claims. Numerical results and comparative claims require the accompanying source review before public reuse.

Read the connected case study or article →

Not stated in document opening · 2026-10-19

Provenance, authorship and editorial notes

02. Architecture

Id

game-platform-docs-02-architecture

Project

game-platform

Source path

game-platform/docs/02-architecture.md

Source sha256

4da32dd1cab66c298c97e30a4885009565d06085fa77378833df3d77cb70f637

Repo head

adfb7831984715cb220e4c3c8ba55a66fc7c54fa

Date

2026-10-19

Date provenance

First date near document opening; not independent publication date

Git last change

2026-10-19T17:21:13+01:00

Author

Not stated in document opening

Document status

Engineering architecture reference

Summary

Describes host, SDK, optional runtime kit and game boundaries, postMessage bridge, saves, identity, AI gate, rooms, code overrides and offline shell.

Topics
  • games
  • architecture
  • AI workflow
Related ids
  • game-platform-docs-11-game-factory
  • game-platform-docs-09-multi-agent-workflow
  • game-platform-docs-04-quality-gates
  • game-platform-proposals-learn-from-play
Evidence concerns
  • Dated 2026-10-19 working-tree reference; SDK capabilities do not imply ANN integration or universal agent autonomy.
Word count

1852

Headings
  • 02. Architecture
  • 1. Layers
  • 2. The hub ↔ game bridge
  • 3. SDK v2 saves
  • 4. Accounts and roles
  • 5. The AI gate
  • 6. db access and db rules
  • 7. Rooms (party play)
  • 8. Code Editor overrides
  • 9. Offline app
  • 10. What a new game must and must not do
Publication status

Publication or peer-reviewed acceptance not established by discovery

Format

md

Private review scan
Known secret pattern hits

0

Customer or player record sets

None identified in selected narrative/design documents; no gameplay records, account databases or hand datasets copied.

Scope

Pattern scan plus document review; not a formal exhaustive privacy audit.

02. Architecture

How a game talks to the platform. All citations are MM/ = maneet-games/master-mind/,
working tree read 2026-10-19.

1. Layers

flowchart TB
  subgraph HOST["HOST (one of: Hub · Offline shell · Game Studio · Standalone)"]
    H["identity, roles, wallet + MM coins, passes/codes, quests,<br/>AI gate, db/room access + per-game scopes,<br/>catalogue, Code Editor overrides"]
  end
  subgraph SDK["SDK v2: games/_sdk/mm-sdk.js (window.MM) — services, no UI"]
    S["ready · data · store · player · caps/hello · errors · rewards · quest"]
  end
  subgraph KIT["optional: games/_kit/kit.js (MMKit) + family layers (games/_ordie/core.js)"]
    K["screens · lifecycle · pause · fit · input · sound · toast/modal"]
  end
  G["GAME: games/&lt;id&gt;/index.html — gameplay, art, tuning, own save shape"]
  G -->|hooks| K --> S
  G -->|plain JS calls| S
  S <-->|"postMessage {__mm:1,id,cap,op,…}"| H

Rule: a layer calls only the layer below it, and only the host touches window.claude capabilities (db, room,
sample, user) (FINAL-PLATFORM-AND-IMPROVEMENTS.md §2.2). Inside the hub, the game iframe has no window.claude.

2. The hub ↔ game bridge

Envelope (MM/games/_sdk/mm-sdk.js header, "Wire protocol"):

game → hub   {__mm:1, id, cap, op, …args}
hub  → game  {__mm:1, reply:id, val, err?:{code, message, retryable?}}
hub  → game  {__mm:1, push:sid, val}                 (subscriptions)
hub  → game  {__mm:1, event:"player", val:{player}}  (a different player signed in)

The handler is one message listener (MM/index.html:3653). In order, it:

1. Drops any message whose source is not #frame or the Code Editor preview #edPrev, or that lacks __mm/id.
2. Stamps a generation ("f"+launchSeq+"."+frameReloads); a reply to a game that has since closed or reloaded is
dropped (:3656-3658).
3. Rejects unknown capabilities and ops with unsupported (:3666-3675). The lists (:3398-3401):

capopswho may call
syshelloonly the open game in #frame
mmready, saveopen game in #frame (save: :3778)
mmquest, pass, rewardsopen game (:3782-3784); pass/rewards are read-only
mmauthzopen game; returns this tab's live {goat, hacker, name} (:3787)
mmboard, telemetry, galleryopen game in #frame (:3767, :3773)
mmplay, role, official, me, passes, buyPass, arc*, giveCoins …Game Studio only (studioSrc, :3121)
aiisGoat, aiAllowed, abort, ordie, judge, sample, ordieImg, ordieScorebehind the AI gate (§5)
hasdb, user, room, samplehas.sample is false unless the AI gate passes (:3668)
userid, isOwner
dbdset, dget, dsub, qget, qsubper-game scopes (§6)
roomjoin, presence, emit, leave, peerssub, onsub§7

Error codes (one list, MM.errors in the SDK): unsupported not_granted invalid_argument guest offline busy
rate_limited timeout too_big player_changed unavailable bad_reply
, plus the hub's ai_off (:3046).

Handshake. MM.caps() sends {cap:"sys",op:"hello",v:2,…}; a v2 host answers its capability map; an older
host's answer (not_granted, invalid_argument, nothing) makes the SDK derive the map from v1 mm.ready
(SDK header, "v2"). v1 ops are never removed: published games and Code Editor versions cannot be recompiled
(decision 6).

sequenceDiagram
  participant G as Game (iframe #frame)
  participant S as mm-sdk.js
  participant H as Hub (index.html)
  participant D as db (window.claude)
  G->>S: MM.ready({game, legacy})
  S->>H: {cap:"mm",op:"ready",v:1,game}
  H-->>S: {player, version, game, quest, cloud}
  S->>H: {cap:"mm",op:"save",action:"get",pid}
  H->>D: read data/users/<uid>/sdk~<game>~<pid>
  H-->>S: {d:{key:{v,t}}}
  S-->>G: resolves (always) → game boots
  G->>S: MM.data.setItem / store.save()
  Note over S: debounce 2 s, or at once on hide/pagehide
  S->>H: {op:"save",action:"put",pid,d}
  H->>D: sdkSerial(path): merge newest-per-key, write
  H-->>S: {d:merged}
  H--)S: {event:"player"} (another player signed in)
  S-->>G: MM.on("player") — MM.data already reloaded

3. SDK v2 saves

From the mm-sdk.js header (version 2.0.0, CLOUD_MAX=100000, MAX_KEYS=500, DEBOUNCE=2000 at :99):

WhereRule
Local copylocalStorage["mmsdk~<game>~<playerId|guest>"] (+ .bak)always
Cloud copydata/users/<uid>/sdk~<game>~<playerId>signed-in hub players only; ≤ 100 KB per game per player, above that device-only
Mergenewest write per key wins; removals are tombstonestimestamps come from device clocks
Write timing2 s after the last change; at once on hide/closeMM.data.flush() forces it

MM.store(key, defaults, migrate): one JSON save object under MM.data[key]. Load = key → key.bak
(recovered) → defaults; migrate runs on a copy; nothing is written before the real save loads; write-if-changed,
previous good copy to .bak; on a player switch the store refills with the new player's save before the game's
MM.on("player") runs, and queued writes of the old player are dropped. A throwing migrate sets store.blocked
and refuses writes (use it for "newer version" saves). store.legacy / store.restoreLegacy() offer an old pre-SDK
save that could not be migrated automatically; only an explicit player action restores it. opts.merge is not
supported: the operator chose newest-wins after five Codex rounds on device merge (decision 23).

Limits to design around: one big key means two devices playing at once keep only the latest copy; split saves into
several keys if that matters. A cached old page may run SDK v1: load mm-store-v1.js after the SDK.

4. Accounts and roles

flowchart LR
  C["Claude account (uid)"] --> P["Master Mind player (name)<br/>mm-player, mm-session {sid, uid, pw}"]
  P --> R{role}
  R -->|"uid ∈ GOAT_UIDS + GOAT name + password verified on this uid + current sid"| GOAT["👑 GOAT: moderation, approvals, AI"]
  R -->|"HACKER name + owner check + current sid"| HK["💻 hacker: AI, no coins/moderation"]
  R -->|"pass bought from wallet"| VIP["👑 VIP / 💎 Premium: cosmetics"]
  R --> N["player: no AI (friend judge)"]
  C -.->|"not signed in"| GU["guest: device-only saves"]

list, cache) never grant GOAT (decision 34). Hacker: isHacker() (:1214).

another tab's login revokes this tab until it signs in again (storage listener, :1208).

written only while isGoat() (adminMarkSync, :1202) and must not be trusted by new games
(06-security.md (06-security.md) L5).

MM.rewards (cosmetic only) (MM/docs/PHASE2-PROFILES-PASSES-CODES.md "Pass mechanics").

5. The AI gate

flowchart LR
  Q["game asks ai.ordie / judge / ordieImg / ordieScore<br/>or has.sample"] --> A{"aiAllowed()<br/>= goatStrict() || hackerStrict()<br/>try/catch → false"}
  A -->|false| OFF["{code:'ai_off'} / has:false → friend judge"]
  A -->|true| L{"same player generation<br/>after every await?"}
  L -->|no| PC["player_changed"]
  L -->|yes| M["model call, reply cleaned;<br/>malformed → bad_reply (never 0 points)"]

(aiAllowed, MM/index.html:3045; roleStrict re-checks playerGen after awaits, :3035).

(comment above ordieImg, :3682).

6. db access and db rules

(MM/index.html:3212):
- dset to a hub-owned collection (BRIDGE_HUB_COLS, :3190) or hub-owned user doc (BRIDGE_HUB_USER, :3195:
wallet~, quests~, sdk~, …) → not_granted.
- Any other dset must match the game's BRIDGE_WRITES globs (:3201); enforcement is on (BRIDGE_ENFORCE=true, :3189).
- Private docs (password hashes, locks, telemetry) cannot even be read (BRIDGE_PRIVATE, :3197).

for shared collections is the artifact's db rules.

in MM/docs/PHASE2-PROFILES-PASSES-CODES.md §"New db rules"; the live rules are snapshotted read-only in
db-rules-snapshot.md (db-rules-snapshot.md) (pattern: read view, write owner; per-player
doc {self} write interact). Every db rule change is an explicit operator decision. Claims that "someone else
can overwrite X" are inference until the live rules are read (FINAL… §8).

(leaseOn) across pages (wallet, boards, gallery; tested by ci/hub-f1-test.mjs).

7. Rooms (party play)

closed game cannot end the new game's room (phase0-test.mjs C47-C49).

(decisions 2, 31); every party has a device-only fallback.

decision 31). Status: not run yet (open, operator 2026-10-19). Do not build live rooms for other games until it has run.

8. Code Editor overrides

A game's live code can come from the database instead of its file.

flowchart LR
  P["Play game g"] --> W["await codeReady (≤ 4 s)"]
  W --> M{"code/&lt;fileKey&gt;.cur ≥ 0?"}
  M -->|no| F["#frame.src = games/&lt;id&gt;/index.html"]
  M -->|yes| C["read codechunks/&lt;fileKey&gt;_v&lt;v&gt;_0..n-1 (field t)"]
  C --> S["#frame.srcdoc = withBase(code) — &lt;base href&gt; injected after &lt;head&gt;"]

number** (codeMeta, MM/index.html:1276; liveCode, :1287; readChunks, :1280; CHUNK=100000, :1275).

decrements cur (:1325-1330).

publish does not change what players get; ship a new db version. Decision 12: fold them back into files.

the chunks back; that pin belongs to the db write tool, not to the hub code (the hub's setPointer at :1324 is a
plain set).

9. Offline app

device; its OPS list is the contract), offline-sync.js (save-file export/import, newest per key), sw.js.

every game and hub op, merge rules, real offline play, sweep of every game).

(ci/hub-addons-test.mjs I1-I2).

10. What a new game must and must not do

MustMust not
boot from MM.ready, save through MM.store/MM.datawrite localStorage progress keys directly
read the player from MM.player, react to MM.on("player")read mm-player / mm-admin to decide anything
ask mm.authz for live role answerskeep its own accounts or passwords
use MM.rewards for passes (cosmetic)sell MM coins or passes, or link out
send MM.event for quests the hub knowssend raw {__mm:1} messages outside _sdk/_kit/_ordie

Continue exploring