# GX Hub Studio — AI-README **You are an AI. This file is written for you.** It is the complete contract for building a game that lives on the GX Hub (https://hub.gxhub.workers.dev) — a hall for games and apps built with AI, with player accounts, friends, avatars, a leaderboard, and **Grokonians**, the hub's points currency. Everything here is verified against the hub's live code. Where a number appears, it is the hub's real number. Follow this file start to finish and your game will pass the hub's automatic scan on the first submission and be ready for its human review. Humans: the friendly version of this document is the guide at `/make-a-game`. This one is the instrument panel. --- ## 1. The shape of a hub game (hard contract) - One folder. `index.html` at its root. No build step, no server code, no database. - Plain HTML, CSS, JavaScript. Canvas is fine. A single-file game is fine. - Every asset lives in the folder and is loaded with a **relative path**. - The game runs inside an **iframe** on the hub's game page: fluid layout that resizes to its frame; take keyboard focus on click; `preventDefault` on arrow keys and space while playing so the hub page does not scroll. - Hosted games are served by the hub under this Content-Security-Policy: `default-src 'self' 'unsafe-inline' blob: data:; connect-src 'none'`. Translation: **your game gets no network.** No fetch, no XHR, no WebSocket, no remote scripts/fonts/images. Design for zero network from the start. (One exception, by design: the hub's own Grokonians SDK script — §3.) - The iframe sandbox is `allow-scripts allow-pointer-lock` for hub-hosted games (linked games also get `allow-same-origin`). You cannot navigate the top window, open popups, or break out of the frame. Do not try — the scan rejects it. ## 2. Packaging limits (hub-enforced, exact) | Rule | Limit | Hub source | |---|---|---| | Zip size | ≤ 25 MB | `LIMITS.maxZipBytes` | | File count | ≤ 500 files | `LIMITS.maxFiles` | | Unzipped total | ≤ 100 MB | upload handler (`total > maxZipBytes*4` → 413) | | Entry point | `index.html` at zip root, or inside one single wrapping folder | `checkEntries` | | Allowed file types | html, htm, js, css, png, jpg, jpeg, webp, gif, svg, ogg, mp3, wav, wasm, json, txt | `LIMITS.exts` | | Paths | no `..`, no leading `/`, no backslashes | `checkEntries` | | Logo | `logo.png` / `logo.jpg` / `logo.webp` at the top (or `"logo"` in `manifest.json`): square, ≥ 256×256, ≤ 200 KB, a real image whose content matches its extension | `findLogo`, `LOGO` | | Title | 3–80 characters | checker + submit form | | Description | 10–1000 characters | checker + submit form | | Submissions | ≤ 5 per day per account | `LIMITS.maxUploadsPerDay` | There are **two doors in**, both ending at the same human review: 1. **Link (open today).** Host the folder anywhere that serves it over https. Submit the URL at `/upload`. The hub fetches the first 512 KB of your page once to check it is alive and embeddable: your host must not send `X-Frame-Options: DENY`/`SAMEORIGIN`, and any CSP `frame-ancestors` must allow https. The hub re-checks linked games; a link failing 3 checks in a row leaves the hall until it recovers. 2. **Zip.** The hub's code accepts zips built to the table above; the zip door is switched by a server flag (`ZIP_UPLOAD`) and is **off today** — build zip-ready anyway, and use the link door to enter. Programmatic submission: create an upload token on your account page and POST the same link form to `/api/v1/submit`. Same scan, same review, same limits. **Review flow, in order:** (1) automatic scan (the rules in §5) — reject-level findings refuse the submission on the spot; (2) a **person** reviews every game that passes — does it run, can it be embedded, is it yours to share, does it play by the rules; (3) on approval the game gets a page at `/game/…`, a card on the home page, and (for SDK games) the "Grokonians connected" badge once the hub sees the handshake. After approval you may add up to 6 screenshots (PNG/JPG/WebP, ≤ 5 MB each) from your game page. Declare honestly what built the game: the submit form's **made with** is one of `grok_bot`, `chatgpt`, `muse`, `claude`, `gemini`, `other` (+ free text for other). ## 3. The Grokonian bridge (exact working protocol) Include the hub's SDK in `index.html`: ```html ``` That is the whole integration surface. What the SDK does, verbatim from its source: it posts `{ type: 'grokonians:hello', version: 1 }` to the hub page (retried every 1 s until answered), the hub replies `{ type: 'grokonians:welcome' }` and follows with `{ type: 'grokonians:balance', balance }` messages; your `Grokonians.earn(n, reason)` / `Grokonians.convert(tokens, reason)` calls are posted to the hub, which alone talks to the API and alone owns the balance. Outside the hub the SDK is inert and `mode` reads `'standalone'`. The hub enforces these caps server-side (`CAPS` in the hub source): | Cap | Value | |---|---| | Per earn event | 1–10, whole numbers only | | Minimum gap between events | 1000 ms ("too fast" → refused) | | Per session | 300 total, ≤ 400 events, session lives 6 hours | | Per game per player per day | 200 | | Sustained rate | ≤ 2/sec average | | Convert | `amount = min(10, floor(tokens × your rate))`; rate set at submit, > 0 and ≤ 10; tokens per call 1–1,000,000 | **Award like this** (the template's queue — copy it; it can never trip a cap): ```js var Bridge = { queued: 0, _timer: null, earn: function (points) { // call with any positive amount points = Math.floor(points); if (!(points > 0)) return; this.queued += points; this._arm(); }, _arm: function () { if (this._timer) return; var self = this; this._timer = setTimeout(function () { self._timer = null; self._flush(); }, 1000); }, _flush: function () { if (this.queued <= 0) return; var n = Math.min(10, this.queued); this.queued -= n; if (window.Grokonians && window.Grokonians.earn) window.Grokonians.earn(n, 'my-reason'); if (this.queued > 0) this._arm(); } }; ``` Show the balance and the connection state: ```js Grokonians.onBalance(function (b) { /* render b */ }); Grokonians.isConnected(); // true after the hub answers hello ``` Design rules the hub rewards: award at **achievements** (level finished, boss down, round survived), not per click; keep reasons short (the hub truncates at 40 chars); never block gameplay on the bridge; never keep a second "real" balance of your own and present it as Grokonians — the hub's number is the only number. Creators earn automatically: **10% of what players earn in your game, +1 per new player per day, capped at 500/day per game.** Grokonians are points, not money: not for sale, not transferable, no cash value. ## 4. The leaderboard contract When a run ends, send the score to the hub page around your frame: ```js window.parent.postMessage( { type: 'scores:submit', score: Math.floor(score), waves: Math.floor(waves) }, 'https://hub.gxhub.workers.dev'); ``` The hub validates (`SCORE_CAPS`: score ≤ 10,000,000, waves ≤ 100,000, ≤ 30 submissions per hour per session), stores it on the game's leaderboard, and replies `{ type: 'scores:submitted', rank }` to your frame if you care to listen. One submission per finished run; `waves` is your game's own progress counter (levels, rounds, stages — 0 if your game has none). ## 5. What the automatic scan rejects (and flags) Ported from the hub's checker. **Reject** = refused on submission: - Text (title, description, filenames, and all text files): extremist terms, hateful phrases, threats/incitement, illegal-goods offers, scam/spam copy ("free money", "double your crypto", "seed phrase", airdrop bait, guaranteed returns), and any claim to be official X / xAI / Grok or endorsed by them. - Files named like X/Twitter/Grok/xAI logos. - Code: crypto miners; packed/obfuscated code (Dean Edwards packer signature, long `\x` escape runs, `_0x…` identifier fields); evaluating decoded or remote code (`eval(atob(...))`, `new Function(fetch...)`, and friends); top-level navigation (`top.location`, `parent.location`, `top.document`); password fields; card/identity fields (cc numbers, cvv, ssn, seed/mnemonic). - Structure: anything in §2's table. **Flagged for the human** (not auto-rejected; expect a look): any `eval` / `new Function`; encoded blobs ≥ 3000 chars; network calls (fetch/XHR/ WebSocket/beacon — dead under the hosted CSP anyway); remote script/iframe/ link URLs (the hub's own SDK tag is the expected exception); popups (`window.open`, `target="_blank"`); adult or graphic terms; image-heavy games (≥ 5 images, or images > 50% of total bytes); the logo itself (a human always looks at it). Content law of the hall, human-enforced: keep it clean for everyone — no hate or extremist content, no threats, no adult or graphic content, no scams, nothing illegal, no impersonation, and it must be **yours to share**. Practical trap, learned from the hub's own code: hub-hosted (zip) games run in a sandbox **without same-origin access** — `localStorage` can throw. Wrap every storage access in try/catch (the template does). Linked games keep their own origin; storage works there. ## 6. Style identity (look like you belong in the hall) The hub is **a stone hall at night**: near-black warm ground `#0d0906`, stone panels `#1a110a`–`#211710`, parchment text `#e8d9b0` (dim `#a08a6a`), one gold `#d9a441` (bright `#f2c14e`, deep `#8a5a1e`), danger `#c0392b`, ok `#8fd06a`. Headings in Georgia/serif, body in system sans. Your game does not have to use the palette — but a game that does feels native the moment the frame opens, and the template ships wearing it. ## 7. The decision checklist (execute in order) 1. **Choose from the catalog.** Open `options.json` (human mirror: `OPTIONS.md`). Pick: one genre, 1–3 mechanics, one control scheme, one art direction, one audio approach, one progression, one difficulty shape, and your hub hooks (earn points and leaderboard are the default pair). 2. **Or start from the template.** `template/` is a complete passing game (Coinfall). Keep its `Bridge` object and its guarded storage; replace the catching game with yours. 3. **Build to the contract in §1–§2.** Single folder, relative paths, zero network, iframe-fluid, focus + preventDefault. 4. **Wire the bridge (§3) and the score (§4).** Test standalone first (the SDK inert, the game complete), then inside any iframe. 5. **Self-check like the hub will.** Run `python3 tools/validate.py `: fix every FAIL; read every WARN and decide with open eyes. Then `python3 tools/package.py ` — it re-validates and refuses a bad zip. 6. **Prepare the listing.** Title (3–80), description (10–1000), square logo (≥256, ≤200 KB), your made-with declaration, a token rate only if your game converts its own currency. 7. **Submit (§2).** Link form at `/upload` (or `/api/v1/submit` with an upload token). Survive the scan, then wait for the human. 8. **After approval.** Add up to 6 screenshots from your game page; keep a linked game's host alive (3 failed checks removes it); watch your creator share arrive on your profile. ## 8. What the Studio gives you (all free) - `AI-README.md` — this file. Also served at `https://hub.gxhub.workers.dev/STUDIO.md`. - `OPTIONS.md` / `options.json` — the options catalog (60 options, 8 categories). Also at `https://hub.gxhub.workers.dev/studio/options.json`. - `template/` — Coinfall, a complete hub-ready starter game that passes the validator with zero warnings. Also downloadable file-by-file from `/studio`. - `tools/validate.py` — this hub's real rules as a runnable check. - `tools/package.py` — validates, then builds the submission zip. *Studio v1. The hub it describes is the hub as it stands today; if the hub's rules move, this file must move with them — the validator cites its sources so you can re-derive any rule from the hub itself.*