Plan: justinkrystal.com games hub (Vite + Phaser + leaderboard)¶
URL: https://mkdocs.justinsforge.com/memory/plans/justinkrystal-games-hub-2026-07-02/
Date: 2026-07-02
Approved spec: A Vite + Phaser 3 + TypeScript games hub for justinkrystal.com. Static build output lands in sites/justinkrystal.com/landing/games/, served by the existing nginx container on media-server LXC 102. First game is Michael's birthday "Set the Hook" fishing reaction game (bass +3 / birthday cake +5 / rabid squirrel -2), emoji art. The birthday landing page at landing/michaels23rd/index.html (sticky banner + message + Immich photo gallery) embeds the game via iframe and is reachable at justinkrystal.com/michaels23rd. Backend leaderboard extends the existing Flask hub-api/app.py with a sqlite-backed store (POST /api/games/score, GET /api/games/leaderboard), HMAC-signed submissions plus server-side sanity caps and per-IP rate limiting for anti-cheat. Dev workflow uses the Vite dev server on Console LAN (192.168.86.50) with HMR. Deploy adds an npm run build step to deploy.sh before the existing tar-and-push.
Out of scope: multiplayer/realtime, user accounts/login, custom (non-emoji) art, any game beyond fishing (hub is structured to add more, only fishing ships now).
Task list¶
Task 1: Scaffold the Vite + Phaser + TS project¶
- Files:
sites/justinkrystal.com/games-src/package.json,vite.config.ts,tsconfig.json,index.html,.gitignore,src/main.ts - What: Create a Vite project (vanilla-ts) with Phaser 3 dependency.
vite.config.tssetsbase: '/games/'andbuild.outDirto../landing/games. Hub index page lists games. - Verification:
cd sites/justinkrystal.com/games-src && npm install && npm run build && test -f ../landing/games/index.html && echo OK - Commit:
feat(justinkrystal): scaffold Vite+Phaser games hub
Task 2: Build the "Set the Hook" fishing game (Phaser scene)¶
- Files:
sites/justinkrystal.com/games-src/src/fishing/FishingScene.ts,src/fishing/main.ts,sites/justinkrystal.com/games-src/fishing.html - What: Phaser scene: cast, randomized bite delay, dip + tap window, weighted catch (bass/cake/squirrel), scoring, too-soon miss, confetti on cake, best score in localStorage.
fishing.htmlis the standalone entry the birthday page iframes. - Verification:
cd sites/justinkrystal.com/games-src && npm run build && test -f ../landing/games/fishing.html && echo OK - Commit:
feat(justinkrystal): Set the Hook fishing game
Task 3: Leaderboard backend (sqlite + Flask endpoints)¶
- Files:
sites/justinkrystal.com/hub-api/app.py - What: Add sqlite store at
/data/leaderboard.db.POST /api/games/score(game, name, score, HMAC sig) with sanity cap + per-IP rate limit + name clamp;GET /api/games/leaderboard?game=fishing&limit=10returns top N. Shared secret fromHUB_SECRET_KEY. - Verification:
cd sites/justinkrystal.com/hub-api && HUB_DATA_FILE=/tmp/hd.json python -c "import app; c=app.app.test_client(); print(c.get('/api/games/leaderboard?game=fishing').status_code)" - Commit:
feat(justinkrystal): sqlite games leaderboard API
Task 4: Wire game to leaderboard + name entry¶
- Files:
sites/justinkrystal.com/games-src/src/fishing/FishingScene.ts,src/fishing/leaderboard.ts - What: On game over, prompt for name, POST signed score, fetch + render top 10. Graceful offline fallback to localStorage-only.
- Verification:
cd sites/justinkrystal.com/games-src && npm run build && grep -q leaderboard ../landing/games/fishing.html && echo OK(bundled ref present) - Commit:
feat(justinkrystal): fishing game leaderboard integration
Task 5: Birthday landing page finalize + game embed¶
- Files:
sites/justinkrystal.com/landing/michaels23rd/index.html - What: Keep banner + message + gallery; replace the inline DOM game with an iframe to
/games/fishing.html. Gallery reads aPHOTOSfilename list fromphotos/. - Verification:
curl -fsS http://127.0.0.1:8091/michaels23rd/ | grep -q 'games/fishing' && echo OK(preview server) - Commit:
feat(justinkrystal): Michael 23rd birthday page with embedded game
Task 6: Deploy pipeline build step¶
- Files:
sites/justinkrystal.com/deploy.sh - What: Prepend
cd games-src && npm ci && npm run build(guarded: skip if node absent, warn loud). hub-api rebuild already handled by compose. Add leaderboard.db to hub-data volume note. - Verification:
bash -n sites/justinkrystal.com/deploy.sh && echo OK - Commit:
chore(justinkrystal): deploy builds games bundle
Task 7: Pull Immich photos into the gallery¶
- Files:
sites/justinkrystal.com/landing/michaels23rd/photos/*,landing/michaels23rd/index.html(PHOTOS array) - What: After Justin pastes Immich asset IDs, download originals via the Immich API over
ssh immich, resize/convert to web jpg, drop inphotos/, list filenames in thePHOTOSarray. - Verification:
ls sites/justinkrystal.com/landing/michaels23rd/photos/*.jpg | wc -lmatches count Justin gave. - Commit:
feat(justinkrystal): add Michael birthday photos
Task 8: Register and document¶
- Files:
MEMORY.md,memory/general/reference_justinkrystal_games_hub.md - What: Topic file documenting the games hub (paths, dev workflow, leaderboard API, deploy, how to add a game), plus MEMORY.md index line. Run eval harness.
- Verification:
bash scripts/forge_eval_run.sh - Commit:
docs(memory): justinkrystal games hub reference