From 9f2cbe177471bb6fbf6b320fcaa48b4a1ac9dfcb Mon Sep 17 00:00:00 2001 From: JOY Date: Fri, 15 May 2026 17:10:17 +0700 Subject: [PATCH 1/8] feat(backend): add Nakama OSS local base --- .claude/CLAUDE.md | 12 ++- .gitignore | 2 + AGENTS.md | 12 ++- backend/nakama/.dockerignore | 5 + backend/nakama/Dockerfile | 17 +++ backend/nakama/README.md | 53 +++++++++ backend/nakama/config/local.yml | 15 +++ backend/nakama/docker-compose.yml | 41 +++++++ backend/nakama/modules/package.json | 15 +++ backend/nakama/modules/src/main.ts | 26 +++++ backend/nakama/modules/tsconfig.json | 16 +++ docs/README.md | 5 +- docs/SUMMARY.md | 3 +- docs/adr/0010-nakama-oss-game-backend.md | 132 +++++++++++++++++++++++ docs/setup/nakama-backend.md | 61 +++++++++++ 15 files changed, 402 insertions(+), 13 deletions(-) create mode 100644 backend/nakama/.dockerignore create mode 100644 backend/nakama/Dockerfile create mode 100644 backend/nakama/README.md create mode 100644 backend/nakama/config/local.yml create mode 100644 backend/nakama/docker-compose.yml create mode 100644 backend/nakama/modules/package.json create mode 100644 backend/nakama/modules/src/main.ts create mode 100644 backend/nakama/modules/tsconfig.json create mode 100644 docs/adr/0010-nakama-oss-game-backend.md create mode 100644 docs/setup/nakama-backend.md diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 7dc1bdc..6800fd9 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -66,12 +66,13 @@ International-friendly framing. Explained via science (Nibirium, biotech, consci ### Backend -- **Supabase Auth** (reuse DOS.Me pattern, do not invent new auth) -- **Supabase Postgres** (durable state: profile, inventory, quest progress, NFT lock state, cultivation tier) -- **Supabase Realtime** (chat global, presence, friend, party invite, notification - NOT for combat / movement sync) -- **Supabase Storage** (avatar, screenshot, UGC) +- **Nakama OSS** (primary game backend: account mapping, server runtime RPCs, social/game metadata, profile, inventory, wallet, leaderboard, matchmaking-adjacent systems) +- **Nakama Postgres** (separate database owned by Nakama; do not point Nakama at the Supabase app database) +- **Supabase Auth** (identity bridge / app account layer, reusing DOS.Me pattern where useful) +- **Supabase Postgres / Storage / Realtime** (app/admin/supporting data only, not authoritative gameplay and not Nakama-owned schema) - **Go LLM Gateway** (reuse DOSRouter pattern, self-host VPS, low-latency) - **Redis** (session, rate limit, transient cache) +- **Hiro / Satori** (deferred; commercial / license-dependent, do not adopt without pricing review and ADR) ### LLM @@ -303,7 +304,7 @@ OUT of scope for vertical slice: 2. **NEVER let LLM mutate authoritative game state.** Server validates all intent. 3. **NEVER put API keys (Anthropic, OpenAI, Convai, ElevenLabs) in Unity client.** All LLM calls go through Go gateway. 4. **NEVER use Host Mode for production.** Server Mode dedicated only. -5. **NEVER add Nakama, OpenAuth, or new auth / social stack.** Reuse Supabase + DOS.Me patterns. +5. **NEVER add OpenAuth or replace the backend / auth / social stack without an ADR and JOY approval.** Nakama OSS is approved as the primary game-backend direction by ADR 0010. Hiro and Satori remain deferred until license and pricing are reviewed. 6. **NEVER change Unity Asset Serialization away from Force Text.** Breaks LFS + diff. 7. **NEVER claim "done" without reviewer pass** (JOY is non-coder, cannot review code himself). 8. **ALWAYS edit BOTH `.claude/CLAUDE.md` and `AGENTS.md` together when updating project context.** They are sister files - Claude Code auto-loads CLAUDE.md, Codex CLI / Cursor / Copilot auto-load AGENTS.md. Edit one without the other = drift; the un-updated file lies to whichever agent reads it. Both files MUST be identical except for the sister-file comment header at line 1. @@ -318,3 +319,4 @@ OUT of scope for vertical slice: - Voice NPC vendor (OpenAI Realtime vs ElevenLabs vs self-host) - Dedicated server hosting (Hetzner specs, region) - Photon Fusion 2 license tier when scaling beyond Cloud free 20 CCU +- Hiro / Satori adoption and pricing diff --git a/.gitignore b/.gitignore index 54c89ff..61787be 100644 --- a/.gitignore +++ b/.gitignore @@ -86,6 +86,8 @@ appsettings.Local.json # Backend / gateway local dev backend/.env backend/node_modules/ +backend/**/node_modules/ +backend/**/build/ backend/dist/ # Photon credentials (if checked-in template) diff --git a/AGENTS.md b/AGENTS.md index 4b8288b..5bff6aa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -66,12 +66,13 @@ International-friendly framing. Explained via science (Nibirium, biotech, consci ### Backend -- **Supabase Auth** (reuse DOS.Me pattern, do not invent new auth) -- **Supabase Postgres** (durable state: profile, inventory, quest progress, NFT lock state, cultivation tier) -- **Supabase Realtime** (chat global, presence, friend, party invite, notification - NOT for combat / movement sync) -- **Supabase Storage** (avatar, screenshot, UGC) +- **Nakama OSS** (primary game backend: account mapping, server runtime RPCs, social/game metadata, profile, inventory, wallet, leaderboard, matchmaking-adjacent systems) +- **Nakama Postgres** (separate database owned by Nakama; do not point Nakama at the Supabase app database) +- **Supabase Auth** (identity bridge / app account layer, reusing DOS.Me pattern where useful) +- **Supabase Postgres / Storage / Realtime** (app/admin/supporting data only, not authoritative gameplay and not Nakama-owned schema) - **Go LLM Gateway** (reuse DOSRouter pattern, self-host VPS, low-latency) - **Redis** (session, rate limit, transient cache) +- **Hiro / Satori** (deferred; commercial / license-dependent, do not adopt without pricing review and ADR) ### LLM @@ -303,7 +304,7 @@ OUT of scope for vertical slice: 2. **NEVER let LLM mutate authoritative game state.** Server validates all intent. 3. **NEVER put API keys (Anthropic, OpenAI, Convai, ElevenLabs) in Unity client.** All LLM calls go through Go gateway. 4. **NEVER use Host Mode for production.** Server Mode dedicated only. -5. **NEVER add Nakama, OpenAuth, or new auth / social stack.** Reuse Supabase + DOS.Me patterns. +5. **NEVER add OpenAuth or replace the backend / auth / social stack without an ADR and JOY approval.** Nakama OSS is approved as the primary game-backend direction by ADR 0010. Hiro and Satori remain deferred until license and pricing are reviewed. 6. **NEVER change Unity Asset Serialization away from Force Text.** Breaks LFS + diff. 7. **NEVER claim "done" without reviewer pass** (JOY is non-coder, cannot review code himself). 8. **ALWAYS edit BOTH `.claude/CLAUDE.md` and `AGENTS.md` together when updating project context.** They are sister files - Claude Code auto-loads CLAUDE.md, Codex CLI / Cursor / Copilot auto-load AGENTS.md. Edit one without the other = drift; the un-updated file lies to whichever agent reads it. Both files MUST be identical except for the sister-file comment header at line 1. @@ -318,3 +319,4 @@ OUT of scope for vertical slice: - Voice NPC vendor (OpenAI Realtime vs ElevenLabs vs self-host) - Dedicated server hosting (Hetzner specs, region) - Photon Fusion 2 license tier when scaling beyond Cloud free 20 CCU +- Hiro / Satori adoption and pricing diff --git a/backend/nakama/.dockerignore b/backend/nakama/.dockerignore new file mode 100644 index 0000000..bddbb41 --- /dev/null +++ b/backend/nakama/.dockerignore @@ -0,0 +1,5 @@ +modules/node_modules/ +modules/build/ +*.log +.env +.env.* diff --git a/backend/nakama/Dockerfile b/backend/nakama/Dockerfile new file mode 100644 index 0000000..152af7a --- /dev/null +++ b/backend/nakama/Dockerfile @@ -0,0 +1,17 @@ +FROM node:20-alpine AS module-builder + +WORKDIR /modules + +RUN apk add --no-cache git + +COPY modules/package*.json ./ +RUN npm install + +COPY modules/tsconfig.json ./ +COPY modules/src ./src +RUN npm run build + +FROM registry.heroiclabs.com/heroiclabs/nakama:3.38.0 + +COPY --from=module-builder /modules/build/*.js /nakama/data/modules/build/ +COPY config/local.yml /nakama/data/local.yml diff --git a/backend/nakama/README.md b/backend/nakama/README.md new file mode 100644 index 0000000..ab92601 --- /dev/null +++ b/backend/nakama/README.md @@ -0,0 +1,53 @@ +# Second Spawn Nakama backend + +Local Nakama OSS setup for SECOND SPAWN. + +This folder stores project configuration and custom runtime modules only. Do not copy or fork Nakama source into this repository. The server is pulled from the official Heroic Labs Docker image. + +## Services + +- Nakama OSS: `registry.heroiclabs.com/heroiclabs/nakama:3.38.0` +- Nakama runtime types: `nakama-common#v1.45.0` +- Postgres: `postgres:16-alpine` + +Nakama owns its own Postgres database. Do not point it at the Supabase app database. + +## Local run + +```powershell +cd backend/nakama +docker compose up --build +``` + +Endpoints: + +- Nakama HTTP API: `http://127.0.0.1:7350` +- Nakama Console: `http://127.0.0.1:7351` +- Console credentials: `admin` / `password` +- Local Postgres: `127.0.0.1:5433` + +## Runtime modules + +Custom server logic lives under `modules/src/`. The Docker build compiles TypeScript to JavaScript and copies the output into `/nakama/data/modules/build` inside the Nakama container. + +Current RPCs: + +- `secondspawn_health` - returns a small JSON health response and proves custom runtime loading. + +The REST RPC payload must be sent as a JSON string. For example, send `"{}"` rather than `{}`. + +## Upgrade policy + +When upgrading Nakama: + +1. Read the official Nakama release notes. +2. Update the Docker image tag in `Dockerfile`. +3. Update `nakama-runtime` in `modules/package.json` to the matching `nakama-common` version from the compatibility matrix. +4. Rebuild locally and verify the health RPC. + +## Boundaries + +- Photon Fusion remains authoritative for gameplay simulation. +- Nakama handles game backend and meta-game services. +- Supabase remains identity / app / admin layer only unless a future ADR changes this. +- Hiro and Satori are deferred until license and pricing review. diff --git a/backend/nakama/config/local.yml b/backend/nakama/config/local.yml new file mode 100644 index 0000000..48ca91a --- /dev/null +++ b/backend/nakama/config/local.yml @@ -0,0 +1,15 @@ +name: second-spawn + +logger: + level: DEBUG + +session: + token_expiry_sec: 7200 + refresh_token_expiry_sec: 604800 + +console: + username: admin + password: password + +runtime: + path: /nakama/data/modules/build diff --git a/backend/nakama/docker-compose.yml b/backend/nakama/docker-compose.yml new file mode 100644 index 0000000..1f8a02b --- /dev/null +++ b/backend/nakama/docker-compose.yml @@ -0,0 +1,41 @@ +services: + postgres: + image: postgres:16-alpine + environment: + POSTGRES_DB: nakama + POSTGRES_USER: nakama + POSTGRES_PASSWORD: localdb + volumes: + - nakama_postgres:/var/lib/postgresql/data + ports: + - "5433:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U nakama -d nakama"] + interval: 3s + timeout: 3s + retries: 10 + + nakama: + build: + context: . + depends_on: + postgres: + condition: service_healthy + entrypoint: + - "/bin/sh" + - "-ecx" + - > + /nakama/nakama migrate up --database.address nakama:localdb@postgres:5432/nakama && + exec /nakama/nakama --config /nakama/data/local.yml --database.address nakama:localdb@postgres:5432/nakama + ports: + - "7349:7349" + - "7350:7350" + - "7351:7351" + healthcheck: + test: ["CMD", "/nakama/nakama", "healthcheck"] + interval: 10s + timeout: 5s + retries: 10 + +volumes: + nakama_postgres: diff --git a/backend/nakama/modules/package.json b/backend/nakama/modules/package.json new file mode 100644 index 0000000..549998f --- /dev/null +++ b/backend/nakama/modules/package.json @@ -0,0 +1,15 @@ +{ + "name": "@second-spawn/nakama-modules", + "version": "0.1.0", + "private": true, + "scripts": { + "build": "tsc", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "nakama-runtime": "https://github.com/heroiclabs/nakama-common#v1.45.0" + }, + "devDependencies": { + "typescript": "5.4.5" + } +} diff --git a/backend/nakama/modules/src/main.ts b/backend/nakama/modules/src/main.ts new file mode 100644 index 0000000..ed64261 --- /dev/null +++ b/backend/nakama/modules/src/main.ts @@ -0,0 +1,26 @@ +const rpcIdHealth = "secondspawn_health"; + +let InitModule: nkruntime.InitModule = function ( + ctx: nkruntime.Context, + logger: nkruntime.Logger, + nk: nkruntime.Nakama, + initializer: nkruntime.Initializer +) { + initializer.registerRpc(rpcIdHealth, rpcHealth); + logger.info("Second Spawn Nakama runtime loaded."); +}; + +const rpcHealth: nkruntime.RpcFunction = function ( + ctx: nkruntime.Context, + logger: nkruntime.Logger, + nk: nkruntime.Nakama, + payload: string +): string { + return JSON.stringify({ + ok: true, + service: "second-spawn-nakama", + userId: ctx.userId || null + }); +}; + +InitModule.bind(null); diff --git a/backend/nakama/modules/tsconfig.json b/backend/nakama/modules/tsconfig.json new file mode 100644 index 0000000..987a02b --- /dev/null +++ b/backend/nakama/modules/tsconfig.json @@ -0,0 +1,16 @@ +{ + "files": [ + "./src/main.ts" + ], + "compilerOptions": { + "target": "es5", + "outFile": "./build/index.js", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "typeRoots": [ + "./node_modules" + ] + } +} diff --git a/docs/README.md b/docs/README.md index 08783ac..81bfdfb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,16 +16,18 @@ This documentation is the canonical public design and architecture source for th ## Current Prototype Focus -The current implementation focus is a thin, networked player-controller prototype: +The current implementation focus is a thin, networked player-controller prototype plus a self-hostable game-backend base: - Minimal Fusion controller first. - Opsive Ultimate Character Controller evaluated after the baseline works. +- Nakama OSS runs locally as the primary game-backend direction. - No large Unity asset imports until the movement, camera, and authority contract are verified. Relevant docs: - [Overview Design](design/06-overview-design.md) - [Networked Player Controller Prototype](design/07-player-controller-prototype.md) +- [Nakama Backend Setup](setup/nakama-backend.md) ## Signature Features @@ -40,4 +42,3 @@ Relevant docs: - Vietnamese notes may live under `docs/vi/` when needed, but English docs remain the source of truth. - If Vietnamese notes and English docs conflict, the English canonical docs win. - `/docs` is public-facing through GitBook. Do not place private credentials, internal-only secrets, or unpublished partner details here. - diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 272492f..53040d3 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -28,10 +28,11 @@ - [ADR 0007: Fusion 2.0.12 Unity 6.5 Beta Incompatibility](adr/0007-photon-fusion-2-0-12-unity-6-5-beta-incompat.md) - [ADR 0008: Codex Primary Agent Workflow](adr/0008-codex-primary-agent-workflow.md) - [ADR 0009: Fusion Networking Assembly Unsafe Code](adr/0009-fusion-networking-assembly-unsafe-code.md) +- [ADR 0010: Nakama OSS Game Backend](adr/0010-nakama-oss-game-backend.md) ## Setup - [Agent Handoff](setup/agent-handoff.md) - [Fusion Install](setup/fusion-install.md) +- [Nakama Backend](setup/nakama-backend.md) - [Unity Conventions](setup/unity-conventions.md) - diff --git a/docs/adr/0010-nakama-oss-game-backend.md b/docs/adr/0010-nakama-oss-game-backend.md new file mode 100644 index 0000000..c4a0131 --- /dev/null +++ b/docs/adr/0010-nakama-oss-game-backend.md @@ -0,0 +1,132 @@ +# ADR 0010: Nakama OSS as primary game backend + +**Status:** Accepted +**Date:** 2026-05-15 +**Decision maker:** JOY + +## Context + +SECOND SPAWN needs more than plain app persistence. The vertical slice already points toward online ARPG / MMO-lite systems: player profiles, inventory, wallet, social state, leaderboards, matchmaking-adjacent flows, server-side validation, and future guild systems. ADR 0002 accepted Supabase because it was the smallest known stack, but further backend review showed a high risk that a Supabase-only approach would become a custom game backend built from scratch. + +Nakama OSS is open source, self-hostable, Unity-compatible, and designed for online games. Hiro and Satori are separate Heroic Labs products with commercial / license dependencies and are not part of this decision. + +## Decision + +Adopt **Nakama OSS** as the primary game backend direction for SECOND SPAWN. + +Initial implementation lives in `backend/nakama/` and uses the official Heroic Labs Docker image, not copied source code or a fork. The monorepo stores only project configuration and custom SECOND SPAWN runtime modules. + +Supabase remains available for identity bridge, app/admin tooling, and DOS.Me ecosystem integration. Nakama owns its own Postgres database and schema. Do not point Nakama at the Supabase app database. + +Photon Fusion remains the authoritative gameplay networking layer. Nakama is not responsible for frame-level movement, combat simulation, or Fusion object authority. + +## Architecture + +```text +Unity client + | Photon Fusion gameplay traffic + |------------------------------------------------> Fusion host / dedicated server + | + | auth / profile / social / meta RPCs + |------------------------------------------------> Nakama OSS + | + v + Nakama Postgres + +Supabase Auth / app data + ^ Go LLM Gateway + | ^ + | identity bridge | NPC / offline-agent LLM calls + +--------------------------+ +``` + +## Scope + +Included now: + +- Local Docker Compose setup for Nakama + Postgres. +- Pinned Nakama version and matching TypeScript runtime dependency. +- Minimal TypeScript runtime module to prove module loading. +- Documentation for local operation and upgrade policy. + +Deferred: + +- Unity SDK integration. +- Supabase JWT verification / custom authentication bridge. +- Inventory, wallet, profile, quest, and SECOND token RPCs. +- Production deployment hardening. +- Hiro, Satori, and Heroic Cloud. + +## Alternatives considered + +### Supabase-only thin backend + +**Pros:** familiar, fast to start, reuses DOS.Me knowledge. + +**Cons:** game-specific features would be custom-built: inventory rules, wallets, matchmaking-adjacent flows, social graph rules, leaderboards, and server-side game RPCs. + +**Rejection reason:** too likely to become a homemade weaker Nakama as the project grows. + +### PlayFab + +**Pros:** mature managed game backend, broad feature set, Microsoft ecosystem. + +**Cons:** managed lock-in, less open-source aligned, less suitable for a public repo that wants self-hostable infrastructure. + +**Rejection reason:** worse fit for open-source ownership and DOS ecosystem control. + +### AccelByte + +**Pros:** strong enterprise game backend. + +**Cons:** heavier vendor relationship and scope than a solo founder vertical slice needs. + +**Rejection reason:** too enterprise-heavy for the current milestone. + +### Nakama OSS + +**Pros:** game-specific backend primitives, self-hostable, open source, Unity SDK, custom runtime, can be run locally and deployed on VPS. + +**Cons:** new operational surface, separate database, and future complexity if Supabase identity bridge is mishandled. + +**Acceptance reason:** best balance of game-backend fit, ownership, and solo-founder maintainability. + +## Consequences + +### Positive + +- Reduces the chance of building core game-backend primitives from scratch. +- Keeps game backend self-hostable and compatible with the open-source project direction. +- Provides a clean place for server-authoritative meta-game RPCs. +- Keeps Fusion focused on gameplay simulation. + +### Negative + +- Adds a second backend database beside Supabase. +- Requires clear ownership boundaries to avoid duplicated profile/inventory state. +- Requires Docker and service operations earlier in the project. + +### Risks + +- **Identity split:** Supabase user IDs and Nakama user IDs can drift. + - Mitigation: define a single mapping contract before Unity login integration. +- **Over-scoping backend too early:** MMORPG backend ambition can overwhelm prototype work. + - Mitigation: only add RPCs needed by the current milestone. +- **Commercial feature temptation:** Hiro/Satori may look convenient but change the cost profile. + - Mitigation: defer until pricing and license review. + +## Validation criteria + +- `docker compose config` succeeds in `backend/nakama/`. +- Nakama and Postgres start locally through Docker Compose. +- Nakama Console is reachable on `127.0.0.1:7351`. +- The custom TypeScript runtime logs a successful load message. +- The `secondspawn_health` RPC returns a JSON health response after a test client authenticates. + +## Related decisions + +- ADR 0001: Photon Fusion 2 +- ADR 0002: Supabase as backend +- ADR 0003: LLM safety architecture +- ADR 0004: AI agent offline control +- ADR 0008: Codex primary agent workflow diff --git a/docs/setup/nakama-backend.md b/docs/setup/nakama-backend.md new file mode 100644 index 0000000..abd202c --- /dev/null +++ b/docs/setup/nakama-backend.md @@ -0,0 +1,61 @@ +# Nakama Backend Setup + +SECOND SPAWN uses Nakama OSS as the primary game-backend direction. The local setup lives in: + +```text +backend/nakama/ +``` + +See [ADR 0010](../adr/0010-nakama-oss-game-backend.md) for the decision. + +## Local Services + +- Nakama OSS: `registry.heroiclabs.com/heroiclabs/nakama:3.38.0` +- Nakama runtime types: `nakama-common#v1.45.0` +- Postgres: `postgres:16-alpine` + +Nakama owns its own Postgres database. Do not point Nakama at the Supabase app database. + +## Run Locally + +```powershell +cd backend/nakama +docker compose up --build +``` + +Endpoints: + +- Nakama HTTP API: `http://127.0.0.1:7350` +- Nakama Console: `http://127.0.0.1:7351` +- Console credentials: `admin` / `password` +- Local Nakama Postgres: `127.0.0.1:5433` + +## Health Check + +Nakama service health: + +```powershell +curl.exe -i http://127.0.0.1:7350/healthcheck +``` + +Runtime RPC smoke check: + +1. Authenticate a local device through Nakama. +2. Call the `secondspawn_health` RPC with a string payload. + +The RPC should return: + +```json +{ + "ok": true, + "service": "second-spawn-nakama", + "userId": "" +} +``` + +## Boundaries + +- Photon Fusion remains authoritative for frame-level gameplay. +- Nakama handles game backend and meta-game services. +- Supabase remains identity / app / admin layer unless a future ADR changes this. +- Hiro and Satori are deferred until license and pricing review. From 3295e2561cfdcc28234101ec2903d752ba045f2d Mon Sep 17 00:00:00 2001 From: JOY Date: Fri, 15 May 2026 17:19:38 +0700 Subject: [PATCH 2/8] chore(backend): pin Postgres and expose Nakama metrics --- backend/nakama/README.md | 7 ++++++- backend/nakama/docker-compose.yml | 5 +++-- docs/adr/0010-nakama-oss-game-backend.md | 3 ++- docs/setup/nakama-backend.md | 7 ++++++- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/backend/nakama/README.md b/backend/nakama/README.md index ab92601..dc741f2 100644 --- a/backend/nakama/README.md +++ b/backend/nakama/README.md @@ -8,7 +8,7 @@ This folder stores project configuration and custom runtime modules only. Do not - Nakama OSS: `registry.heroiclabs.com/heroiclabs/nakama:3.38.0` - Nakama runtime types: `nakama-common#v1.45.0` -- Postgres: `postgres:16-alpine` +- Postgres: `postgres:16.14-alpine` Nakama owns its own Postgres database. Do not point it at the Supabase app database. @@ -23,6 +23,7 @@ Endpoints: - Nakama HTTP API: `http://127.0.0.1:7350` - Nakama Console: `http://127.0.0.1:7351` +- Nakama Prometheus metrics: `http://127.0.0.1:9100` - Console credentials: `admin` / `password` - Local Postgres: `127.0.0.1:5433` @@ -51,3 +52,7 @@ When upgrading Nakama: - Nakama handles game backend and meta-game services. - Supabase remains identity / app / admin layer only unless a future ADR changes this. - Hiro and Satori are deferred until license and pricing review. + +## Alerting + +Nakama exposes Prometheus metrics on port `9100`. Telegram alerts should be wired through Prometheus Alertmanager or Grafana Alerting with secrets stored outside Git. diff --git a/backend/nakama/docker-compose.yml b/backend/nakama/docker-compose.yml index 1f8a02b..03cae76 100644 --- a/backend/nakama/docker-compose.yml +++ b/backend/nakama/docker-compose.yml @@ -1,6 +1,6 @@ services: postgres: - image: postgres:16-alpine + image: postgres:16.14-alpine environment: POSTGRES_DB: nakama POSTGRES_USER: nakama @@ -26,11 +26,12 @@ services: - "-ecx" - > /nakama/nakama migrate up --database.address nakama:localdb@postgres:5432/nakama && - exec /nakama/nakama --config /nakama/data/local.yml --database.address nakama:localdb@postgres:5432/nakama + exec /nakama/nakama --config /nakama/data/local.yml --database.address nakama:localdb@postgres:5432/nakama --metrics.prometheus_port 9100 ports: - "7349:7349" - "7350:7350" - "7351:7351" + - "9100:9100" healthcheck: test: ["CMD", "/nakama/nakama", "healthcheck"] interval: 10s diff --git a/docs/adr/0010-nakama-oss-game-backend.md b/docs/adr/0010-nakama-oss-game-backend.md index c4a0131..4a3f24f 100644 --- a/docs/adr/0010-nakama-oss-game-backend.md +++ b/docs/adr/0010-nakama-oss-game-backend.md @@ -45,6 +45,7 @@ Supabase Auth / app data Included now: - Local Docker Compose setup for Nakama + Postgres. +- Nakama Prometheus metrics port exposed for future monitoring and alerting. - Pinned Nakama version and matching TypeScript runtime dependency. - Minimal TypeScript runtime module to prove module loading. - Documentation for local operation and upgrade policy. @@ -54,7 +55,7 @@ Deferred: - Unity SDK integration. - Supabase JWT verification / custom authentication bridge. - Inventory, wallet, profile, quest, and SECOND token RPCs. -- Production deployment hardening. +- Production deployment hardening, including Prometheus Alertmanager or Grafana Telegram alerts. - Hiro, Satori, and Heroic Cloud. ## Alternatives considered diff --git a/docs/setup/nakama-backend.md b/docs/setup/nakama-backend.md index abd202c..b9faccf 100644 --- a/docs/setup/nakama-backend.md +++ b/docs/setup/nakama-backend.md @@ -12,7 +12,7 @@ See [ADR 0010](../adr/0010-nakama-oss-game-backend.md) for the decision. - Nakama OSS: `registry.heroiclabs.com/heroiclabs/nakama:3.38.0` - Nakama runtime types: `nakama-common#v1.45.0` -- Postgres: `postgres:16-alpine` +- Postgres: `postgres:16.14-alpine` Nakama owns its own Postgres database. Do not point Nakama at the Supabase app database. @@ -27,6 +27,7 @@ Endpoints: - Nakama HTTP API: `http://127.0.0.1:7350` - Nakama Console: `http://127.0.0.1:7351` +- Nakama Prometheus metrics: `http://127.0.0.1:9100` - Console credentials: `admin` / `password` - Local Nakama Postgres: `127.0.0.1:5433` @@ -59,3 +60,7 @@ The RPC should return: - Nakama handles game backend and meta-game services. - Supabase remains identity / app / admin layer unless a future ADR changes this. - Hiro and Satori are deferred until license and pricing review. + +## Alerting + +Prometheus can scrape Nakama metrics from port `9100`. Telegram notifications should be handled by Prometheus Alertmanager or Grafana Alerting. Bot tokens and chat IDs must stay in local secrets, not in Git. From 492c819b063ff4b7e5ef9a6abf62a07d9c4cc209 Mon Sep 17 00:00:00 2001 From: JOY Date: Fri, 15 May 2026 19:02:39 +0700 Subject: [PATCH 3/8] docs(backend): record Supabase schema mode --- backend/nakama/README.md | 13 ++++++++ docs/adr/0010-nakama-oss-game-backend.md | 7 +++-- docs/setup/nakama-backend.md | 39 ++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/backend/nakama/README.md b/backend/nakama/README.md index dc741f2..51a4f1a 100644 --- a/backend/nakama/README.md +++ b/backend/nakama/README.md @@ -53,6 +53,19 @@ When upgrading Nakama: - Supabase remains identity / app / admin layer only unless a future ADR changes this. - Hiro and Satori are deferred until license and pricing review. +## Supabase Schema-Isolated Mode + +For MVP production, Nakama may use a Supabase project through the Session Pooler if it has a dedicated role and an isolated schema. + +Verified shape: + +- Schema: `second` +- Role: `nakama_second` +- Pooler: Session Pooler on port `5432` +- Search path: `second, public` + +Do not use the Supabase `postgres` role for Nakama in production. Do not commit the connection string. + ## Alerting Nakama exposes Prometheus metrics on port `9100`. Telegram alerts should be wired through Prometheus Alertmanager or Grafana Alerting with secrets stored outside Git. diff --git a/docs/adr/0010-nakama-oss-game-backend.md b/docs/adr/0010-nakama-oss-game-backend.md index 4a3f24f..bd91c07 100644 --- a/docs/adr/0010-nakama-oss-game-backend.md +++ b/docs/adr/0010-nakama-oss-game-backend.md @@ -16,7 +16,7 @@ Adopt **Nakama OSS** as the primary game backend direction for SECOND SPAWN. Initial implementation lives in `backend/nakama/` and uses the official Heroic Labs Docker image, not copied source code or a fork. The monorepo stores only project configuration and custom SECOND SPAWN runtime modules. -Supabase remains available for identity bridge, app/admin tooling, and DOS.Me ecosystem integration. Nakama owns its own Postgres database and schema. Do not point Nakama at the Supabase app database. +Supabase remains available for identity bridge, app/admin tooling, and DOS.Me ecosystem integration. Nakama owns its own database schema. The preferred clean production shape is a separate Nakama Postgres database, but a Supabase project can host Nakama for MVP if Nakama uses a dedicated role and the isolated `second` schema. Photon Fusion remains the authoritative gameplay networking layer. Nakama is not responsible for frame-level movement, combat simulation, or Fusion object authority. @@ -46,6 +46,7 @@ Included now: - Local Docker Compose setup for Nakama + Postgres. - Nakama Prometheus metrics port exposed for future monitoring and alerting. +- Verified Supabase Session Pooler schema-isolated mode using schema `second`. - Pinned Nakama version and matching TypeScript runtime dependency. - Minimal TypeScript runtime module to prove module loading. - Documentation for local operation and upgrade policy. @@ -54,6 +55,7 @@ Deferred: - Unity SDK integration. - Supabase JWT verification / custom authentication bridge. +- Production secret rotation and non-default Nakama keys. - Inventory, wallet, profile, quest, and SECOND token RPCs. - Production deployment hardening, including Prometheus Alertmanager or Grafana Telegram alerts. - Hiro, Satori, and Heroic Cloud. @@ -103,7 +105,7 @@ Deferred: ### Negative -- Adds a second backend database beside Supabase. +- Adds a second backend database beside Supabase, unless MVP production uses a schema-isolated Supabase project. - Requires clear ownership boundaries to avoid duplicated profile/inventory state. - Requires Docker and service operations earlier in the project. @@ -123,6 +125,7 @@ Deferred: - Nakama Console is reachable on `127.0.0.1:7351`. - The custom TypeScript runtime logs a successful load message. - The `secondspawn_health` RPC returns a JSON health response after a test client authenticates. +- Supabase Session Pooler mode can run Nakama migrations into schema `second`, start Nakama, and serve `secondspawn_health`. ## Related decisions diff --git a/docs/setup/nakama-backend.md b/docs/setup/nakama-backend.md index b9faccf..5592ce0 100644 --- a/docs/setup/nakama-backend.md +++ b/docs/setup/nakama-backend.md @@ -61,6 +61,45 @@ The RPC should return: - Supabase remains identity / app / admin layer unless a future ADR changes this. - Hiro and Satori are deferred until license and pricing review. +## Supabase Schema-Isolated Mode + +For MVP production, Nakama can run against a Supabase project if it uses a dedicated role and isolated schema. + +Tested on 2026-05-15: + +- Supabase Session Pooler worked on port `5432`. +- Schema name: `second`. +- Role name: `nakama_second`. +- Nakama 3.38.0 migrations created 20 tables in schema `second`. +- Nakama server started against the Supabase pooler. +- `secondspawn_health` RPC returned a valid response. + +Setup shape: + +```sql +create schema if not exists second; +create role nakama_second login password ''; +grant connect on database postgres to nakama_second; +grant usage, create on schema second to nakama_second; +alter role nakama_second in database postgres set search_path = second, public; +``` + +Use the Supabase **Session Pooler** URI for Nakama. Do not use Transaction Pooler for the running Nakama service. + +Connection username format: + +```text +nakama_second. +``` + +Connection address shape: + +```text +nakama_second.:@.pooler.supabase.com:5432/postgres?sslmode=require +``` + +Do not commit this connection string. Store it in production secrets. + ## Alerting Prometheus can scrape Nakama metrics from port `9100`. Telegram notifications should be handled by Prometheus Alertmanager or Grafana Alerting. Bot tokens and chat IDs must stay in local secrets, not in Git. From 1a93ac2a824a6f5963f08c80faa4faf152e8a079 Mon Sep 17 00:00:00 2001 From: JOY Date: Fri, 15 May 2026 19:28:24 +0700 Subject: [PATCH 4/8] docs(backend): clarify Nakama Supabase architecture --- .claude/CLAUDE.md | 6 +-- AGENTS.md | 6 +-- docs/ARCHITECTURE.md | 65 ++++++++++++++++-------- docs/adr/0010-nakama-oss-game-backend.md | 12 +++-- 4 files changed, 59 insertions(+), 30 deletions(-) diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 6800fd9..432fb0d 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -67,9 +67,9 @@ International-friendly framing. Explained via science (Nibirium, biotech, consci ### Backend - **Nakama OSS** (primary game backend: account mapping, server runtime RPCs, social/game metadata, profile, inventory, wallet, leaderboard, matchmaking-adjacent systems) -- **Nakama Postgres** (separate database owned by Nakama; do not point Nakama at the Supabase app database) +- **Nakama database** (MVP production may use the Supabase project with dedicated role `nakama_second` and private schema `second`; local dev uses Docker Postgres; keep Nakama-owned tables behind Nakama APIs) - **Supabase Auth** (identity bridge / app account layer, reusing DOS.Me pattern where useful) -- **Supabase Postgres / Storage / Realtime** (app/admin/supporting data only, not authoritative gameplay and not Nakama-owned schema) +- **Supabase Postgres / Storage / Realtime** (app/admin/supporting data only; Supabase can host Nakama schema `second`, but Unity clients must not read or write Nakama tables directly through Supabase APIs) - **Go LLM Gateway** (reuse DOSRouter pattern, self-host VPS, low-latency) - **Redis** (session, rate limit, transient cache) - **Hiro / Satori** (deferred; commercial / license-dependent, do not adopt without pricing review and ADR) @@ -304,7 +304,7 @@ OUT of scope for vertical slice: 2. **NEVER let LLM mutate authoritative game state.** Server validates all intent. 3. **NEVER put API keys (Anthropic, OpenAI, Convai, ElevenLabs) in Unity client.** All LLM calls go through Go gateway. 4. **NEVER use Host Mode for production.** Server Mode dedicated only. -5. **NEVER add OpenAuth or replace the backend / auth / social stack without an ADR and JOY approval.** Nakama OSS is approved as the primary game-backend direction by ADR 0010. Hiro and Satori remain deferred until license and pricing are reviewed. +5. **NEVER add OpenAuth or replace the backend / auth / social stack without an ADR and JOY approval.** Nakama OSS is approved as the primary game-backend direction by ADR 0010. Supabase schema `second` is an acceptable MVP production host for Nakama if accessed only by a dedicated Nakama role. Hiro and Satori remain deferred until license and pricing are reviewed. 6. **NEVER change Unity Asset Serialization away from Force Text.** Breaks LFS + diff. 7. **NEVER claim "done" without reviewer pass** (JOY is non-coder, cannot review code himself). 8. **ALWAYS edit BOTH `.claude/CLAUDE.md` and `AGENTS.md` together when updating project context.** They are sister files - Claude Code auto-loads CLAUDE.md, Codex CLI / Cursor / Copilot auto-load AGENTS.md. Edit one without the other = drift; the un-updated file lies to whichever agent reads it. Both files MUST be identical except for the sister-file comment header at line 1. diff --git a/AGENTS.md b/AGENTS.md index 5bff6aa..32f73fe 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -67,9 +67,9 @@ International-friendly framing. Explained via science (Nibirium, biotech, consci ### Backend - **Nakama OSS** (primary game backend: account mapping, server runtime RPCs, social/game metadata, profile, inventory, wallet, leaderboard, matchmaking-adjacent systems) -- **Nakama Postgres** (separate database owned by Nakama; do not point Nakama at the Supabase app database) +- **Nakama database** (MVP production may use the Supabase project with dedicated role `nakama_second` and private schema `second`; local dev uses Docker Postgres; keep Nakama-owned tables behind Nakama APIs) - **Supabase Auth** (identity bridge / app account layer, reusing DOS.Me pattern where useful) -- **Supabase Postgres / Storage / Realtime** (app/admin/supporting data only, not authoritative gameplay and not Nakama-owned schema) +- **Supabase Postgres / Storage / Realtime** (app/admin/supporting data only; Supabase can host Nakama schema `second`, but Unity clients must not read or write Nakama tables directly through Supabase APIs) - **Go LLM Gateway** (reuse DOSRouter pattern, self-host VPS, low-latency) - **Redis** (session, rate limit, transient cache) - **Hiro / Satori** (deferred; commercial / license-dependent, do not adopt without pricing review and ADR) @@ -304,7 +304,7 @@ OUT of scope for vertical slice: 2. **NEVER let LLM mutate authoritative game state.** Server validates all intent. 3. **NEVER put API keys (Anthropic, OpenAI, Convai, ElevenLabs) in Unity client.** All LLM calls go through Go gateway. 4. **NEVER use Host Mode for production.** Server Mode dedicated only. -5. **NEVER add OpenAuth or replace the backend / auth / social stack without an ADR and JOY approval.** Nakama OSS is approved as the primary game-backend direction by ADR 0010. Hiro and Satori remain deferred until license and pricing are reviewed. +5. **NEVER add OpenAuth or replace the backend / auth / social stack without an ADR and JOY approval.** Nakama OSS is approved as the primary game-backend direction by ADR 0010. Supabase schema `second` is an acceptable MVP production host for Nakama if accessed only by a dedicated Nakama role. Hiro and Satori remain deferred until license and pricing are reviewed. 6. **NEVER change Unity Asset Serialization away from Force Text.** Breaks LFS + diff. 7. **NEVER claim "done" without reviewer pass** (JOY is non-coder, cannot review code himself). 8. **ALWAYS edit BOTH `.claude/CLAUDE.md` and `AGENTS.md` together when updating project context.** They are sister files - Claude Code auto-loads CLAUDE.md, Codex CLI / Cursor / Copilot auto-load AGENTS.md. Edit one without the other = drift; the un-updated file lies to whichever agent reads it. Both files MUST be identical except for the sister-file comment header at line 1. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index bb35889..19d9484 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -23,21 +23,29 @@ High-level architecture overview. For detailed component design see `docs/design | | | | LLM intent request v v - +---------------+ +----------------------------+ - | Supabase | | Go LLM Gateway | - | - Auth | | - Convai (phase 1) | - | - Postgres | | - Anthropic + OpenAI (P2) | - | - Realtime | | - RAG memory (pgvector) | - | - Storage | | - Rate limit + safety | - +---------------+ +-------------+--------------+ - | | - v v - +---------------+ +----------------------------+ - | DOS Chain | | Redis | - | (NFT, wallet) | | - Session | - | via thirdweb | | - Rate limit | - | | | - Transient cache | - +---------------+ +----------------------------+ + +------------------------------+ +----------------------------+ + | Nakama OSS | | Go LLM Gateway | + | - Game API backend | | - Convai (phase 1) | + | - Runtime RPC validation | | - Anthropic + OpenAI (P2) | + | - Profile / inventory | | - RAG memory (pgvector) | + | - Wallet / leaderboard | | - Rate limit + safety | + +--------------+---------------+ +-------------+--------------+ + | | + v v + +------------------------------+ +----------------------------+ + | Supabase | | Redis | + | - Auth / identity | | - Session | + | - Schema `second` for Nakama | | - Rate limit | + | - App/admin data | | - Transient cache | + | - Storage / Realtime | +----------------------------+ + +--------------+---------------+ + | + v + +---------------+ + | DOS Chain | + | (NFT, wallet) | + | via thirdweb | + +---------------+ ``` ## Component Responsibilities @@ -45,7 +53,8 @@ High-level architecture overview. For detailed component design see `docs/design ### Unity Client - Render, input, local prediction -- Communicates only with Fusion server and Supabase Auth +- Communicates with Fusion server for gameplay, Supabase Auth for identity, and Nakama APIs for game backend state +- NEVER reads or writes Nakama-owned tables directly through Supabase REST or Realtime - NEVER calls LLM API directly - NEVER holds API keys - Receives state updates via Fusion tick @@ -55,7 +64,7 @@ High-level architecture overview. For detailed component design see `docs/design - Source of truth for in-zone state (position, HP, combat, drops) - Source of truth for `BodyTime` earn, spend, drain, transfer, and expiration - Validates every action intent (from player input or AI agent) -- Persists durable state to Supabase Postgres (snapshots + events) +- Persists durable meta-game state through Nakama server RPCs, not direct client writes - Triggers LLM gateway for NPC dialogue when triggered - Manages zone lifecycle (load / unload / spawn) - Tick rate: 30Hz (60Hz for boss encounters if needed) @@ -68,12 +77,23 @@ High-level architecture overview. For detailed component design see `docs/design - Subject to same server validation as a real player - Inherits player's character cultivation tier + persona + history -### Supabase Backend +### Nakama Game Backend -- **Auth:** Reuse DOS.Me pattern (email / wallet / OAuth) -- **Postgres:** durable state (profile, inventory, quest progress, NFT lock state, cultivation tier, character history, reincarnation and time-as-currency events) -- **Realtime:** chat global, presence, friend list, party invite, notification (NOT combat / movement) +- Primary API backend for game-owned state +- Runtime RPCs for profile, inventory, wallet, leaderboard, social, and future matchmaking-adjacent systems +- Owns schema `second` in Supabase for MVP production, accessed by dedicated role `nakama_second` +- Local dev uses Docker Postgres so developers can reset Nakama data without touching Supabase +- Unity and tools talk to Nakama APIs, not directly to Nakama tables +- Prometheus metrics exposed for monitoring and future Telegram alerts through Alertmanager or Grafana +- Hiro and Satori are deferred until license and pricing review + +### Supabase Platform + +- **Auth:** identity provider and JWT issuer, reused from DOS.Me patterns where practical +- **Postgres:** hosts app/admin data and may host Nakama-owned schema `second` for MVP production +- **Realtime:** app-side chat, presence, friend list, party invite, notification only when not better handled by Nakama or Fusion - **Storage:** avatar, screenshot, UGC +- **Security boundary:** schema `second` is private backend data; do not expose it to anon/authenticated Supabase client roles ### Go LLM Gateway (DOSRouter pattern) @@ -107,6 +127,8 @@ High-level architecture overview. For detailed component design see `docs/design 4. **NFT lock is on-chain.** When equipped, escrow contract holds. Server reads on-chain state, does not assume off-chain. 5. **AI agent inherits player limits.** No agent can do what a real player cannot. 6. **Time mutations are server-authoritative.** `BodyTime` is gameplay state; client, LLM, and AI agents can only request validated time intents. +7. **Nakama-owned tables are private.** Unity clients never access schema `second` through Supabase APIs; Nakama is the API boundary. +8. **Supabase schema isolation is an MVP production option.** It is acceptable only with dedicated role `nakama_second`, Session Pooler or direct persistent connection, rotated secrets, and non-default Nakama keys. ## Open Architecture Questions @@ -115,3 +137,4 @@ High-level architecture overview. For detailed component design see `docs/design - LLM cost cap per player per day (need to design) - Hot reload for cultivation balance changes (config in Postgres vs git) - Replay system for cheat investigation (record Fusion ticks) +- Whether schema `second` remains in Supabase long term or moves to a separate managed Postgres for operational isolation diff --git a/docs/adr/0010-nakama-oss-game-backend.md b/docs/adr/0010-nakama-oss-game-backend.md index bd91c07..27c0e7a 100644 --- a/docs/adr/0010-nakama-oss-game-backend.md +++ b/docs/adr/0010-nakama-oss-game-backend.md @@ -16,7 +16,7 @@ Adopt **Nakama OSS** as the primary game backend direction for SECOND SPAWN. Initial implementation lives in `backend/nakama/` and uses the official Heroic Labs Docker image, not copied source code or a fork. The monorepo stores only project configuration and custom SECOND SPAWN runtime modules. -Supabase remains available for identity bridge, app/admin tooling, and DOS.Me ecosystem integration. Nakama owns its own database schema. The preferred clean production shape is a separate Nakama Postgres database, but a Supabase project can host Nakama for MVP if Nakama uses a dedicated role and the isolated `second` schema. +Supabase remains available for identity bridge, app/admin tooling, and DOS.Me ecosystem integration. Nakama owns its own database schema. For MVP production, the accepted shape is to host Nakama inside the Supabase project using dedicated role `nakama_second` and isolated schema `second`. This keeps operations simple while preserving a clean API boundary. A separate managed Postgres is a later operational isolation option, not a scale fix by itself. Photon Fusion remains the authoritative gameplay networking layer. Nakama is not responsible for frame-level movement, combat simulation, or Fusion object authority. @@ -31,7 +31,7 @@ Unity client |------------------------------------------------> Nakama OSS | v - Nakama Postgres + Supabase Postgres schema `second` Supabase Auth / app data ^ Go LLM Gateway @@ -102,12 +102,14 @@ Deferred: - Keeps game backend self-hostable and compatible with the open-source project direction. - Provides a clean place for server-authoritative meta-game RPCs. - Keeps Fusion focused on gameplay simulation. +- Avoids an extra managed Postgres service for MVP production by using schema isolation in Supabase. ### Negative -- Adds a second backend database beside Supabase, unless MVP production uses a schema-isolated Supabase project. +- Adds Nakama as a backend API layer and operational surface. - Requires clear ownership boundaries to avoid duplicated profile/inventory state. - Requires Docker and service operations earlier in the project. +- Requires discipline to keep schema `second` private and inaccessible through client-side Supabase APIs. ### Risks @@ -117,6 +119,10 @@ Deferred: - Mitigation: only add RPCs needed by the current milestone. - **Commercial feature temptation:** Hiro/Satori may look convenient but change the cost profile. - Mitigation: defer until pricing and license review. +- **Supabase schema exposure:** Supabase warns that RLS is disabled on Nakama tables. + - Mitigation: do not expose schema `second` through Supabase client APIs; treat it as private backend data accessed only by Nakama role `nakama_second`. +- **Shared database blast radius:** Nakama and app data share the Supabase project in MVP production. + - Mitigation: monitor connection and query pressure, keep backups clear, and move schema `second` to a separate managed Postgres only if operational isolation becomes necessary. ## Validation criteria From 5a3dc996de15feb2ee945676bc1231a403987709 Mon Sep 17 00:00:00 2001 From: JOY Date: Fri, 15 May 2026 19:29:54 +0700 Subject: [PATCH 5/8] chore(backend): add Supabase Nakama compose profile --- backend/nakama/.env.example | 7 +++++++ backend/nakama/README.md | 8 ++++++++ backend/nakama/docker-compose.supabase.yml | 22 ++++++++++++++++++++++ docs/setup/nakama-backend.md | 9 +++++++++ 4 files changed, 46 insertions(+) create mode 100644 backend/nakama/.env.example create mode 100644 backend/nakama/docker-compose.supabase.yml diff --git a/backend/nakama/.env.example b/backend/nakama/.env.example new file mode 100644 index 0000000..5bfd130 --- /dev/null +++ b/backend/nakama/.env.example @@ -0,0 +1,7 @@ +# Copy this file to `.env` for local Supabase-backed Nakama testing. +# Do not commit `.env`. + +# Use the Supabase Session Pooler or a direct persistent connection. +# Format: +# nakama_second.:@.pooler.supabase.com:5432/postgres?sslmode=require +NAKAMA_DATABASE_ADDRESS= diff --git a/backend/nakama/README.md b/backend/nakama/README.md index 51a4f1a..ede4ad4 100644 --- a/backend/nakama/README.md +++ b/backend/nakama/README.md @@ -66,6 +66,14 @@ Verified shape: Do not use the Supabase `postgres` role for Nakama in production. Do not commit the connection string. +Local run against Supabase: + +```powershell +Copy-Item .env.example .env +# Fill NAKAMA_DATABASE_ADDRESS in .env with the Session Pooler connection string. +docker compose -f docker-compose.supabase.yml up --build +``` + ## Alerting Nakama exposes Prometheus metrics on port `9100`. Telegram alerts should be wired through Prometheus Alertmanager or Grafana Alerting with secrets stored outside Git. diff --git a/backend/nakama/docker-compose.supabase.yml b/backend/nakama/docker-compose.supabase.yml new file mode 100644 index 0000000..100bbdd --- /dev/null +++ b/backend/nakama/docker-compose.supabase.yml @@ -0,0 +1,22 @@ +services: + nakama: + build: + context: . + environment: + NAKAMA_DATABASE_ADDRESS: ${NAKAMA_DATABASE_ADDRESS:-} + entrypoint: + - "/bin/sh" + - "-ecx" + - > + : "$${NAKAMA_DATABASE_ADDRESS:?Set NAKAMA_DATABASE_ADDRESS in backend/nakama/.env}" && + exec /nakama/nakama --config /nakama/data/local.yml --database.address "$$NAKAMA_DATABASE_ADDRESS" --metrics.prometheus_port 9100 + ports: + - "7349:7349" + - "7350:7350" + - "7351:7351" + - "9100:9100" + healthcheck: + test: ["CMD", "/nakama/nakama", "healthcheck"] + interval: 10s + timeout: 5s + retries: 10 diff --git a/docs/setup/nakama-backend.md b/docs/setup/nakama-backend.md index 5592ce0..c9315c4 100644 --- a/docs/setup/nakama-backend.md +++ b/docs/setup/nakama-backend.md @@ -100,6 +100,15 @@ nakama_second.:@.pooler.supabase.com:5432/ Do not commit this connection string. Store it in production secrets. +Local run against Supabase: + +```powershell +cd backend/nakama +Copy-Item .env.example .env +# Fill NAKAMA_DATABASE_ADDRESS in .env with the Session Pooler connection string. +docker compose -f docker-compose.supabase.yml up --build +``` + ## Alerting Prometheus can scrape Nakama metrics from port `9100`. Telegram notifications should be handled by Prometheus Alertmanager or Grafana Alerting. Bot tokens and chat IDs must stay in local secrets, not in Git. From b4be69190a5705f58efe1d011cc47590a8e34e2c Mon Sep 17 00:00:00 2001 From: JOY Date: Fri, 15 May 2026 20:08:29 +0700 Subject: [PATCH 6/8] docs(backend): document Nakama production secrets --- backend/nakama/.env.example | 9 +++++++++ backend/nakama/README.md | 12 ++++++++++++ docs/adr/0010-nakama-oss-game-backend.md | 2 +- docs/setup/nakama-backend.md | 12 ++++++++++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/backend/nakama/.env.example b/backend/nakama/.env.example index 5bfd130..b4a848e 100644 --- a/backend/nakama/.env.example +++ b/backend/nakama/.env.example @@ -5,3 +5,12 @@ # Format: # nakama_second.:@.pooler.supabase.com:5432/postgres?sslmode=require NAKAMA_DATABASE_ADDRESS= + +# Required before any production deployment. Local dev may keep config/local.yml defaults. +NAKAMA_CONSOLE_USERNAME= +NAKAMA_CONSOLE_PASSWORD= +NAKAMA_CONSOLE_SIGNING_KEY= +NAKAMA_SOCKET_SERVER_KEY= +NAKAMA_SESSION_ENCRYPTION_KEY= +NAKAMA_SESSION_REFRESH_ENCRYPTION_KEY= +NAKAMA_RUNTIME_HTTP_KEY= diff --git a/backend/nakama/README.md b/backend/nakama/README.md index ede4ad4..7f72c8e 100644 --- a/backend/nakama/README.md +++ b/backend/nakama/README.md @@ -74,6 +74,18 @@ Copy-Item .env.example .env docker compose -f docker-compose.supabase.yml up --build ``` +Before deploying outside local dev, also set non-default values for: + +- `NAKAMA_CONSOLE_USERNAME` +- `NAKAMA_CONSOLE_PASSWORD` +- `NAKAMA_CONSOLE_SIGNING_KEY` +- `NAKAMA_SOCKET_SERVER_KEY` +- `NAKAMA_SESSION_ENCRYPTION_KEY` +- `NAKAMA_SESSION_REFRESH_ENCRYPTION_KEY` +- `NAKAMA_RUNTIME_HTTP_KEY` + +Do not reuse the local `admin` / `password` console credentials in production. + ## Alerting Nakama exposes Prometheus metrics on port `9100`. Telegram alerts should be wired through Prometheus Alertmanager or Grafana Alerting with secrets stored outside Git. diff --git a/docs/adr/0010-nakama-oss-game-backend.md b/docs/adr/0010-nakama-oss-game-backend.md index 27c0e7a..e6ee82c 100644 --- a/docs/adr/0010-nakama-oss-game-backend.md +++ b/docs/adr/0010-nakama-oss-game-backend.md @@ -55,7 +55,7 @@ Deferred: - Unity SDK integration. - Supabase JWT verification / custom authentication bridge. -- Production secret rotation and non-default Nakama keys. +- Production secret rotation and non-default Nakama keys. The secret names are documented in `backend/nakama/.env.example`, but values are not committed. - Inventory, wallet, profile, quest, and SECOND token RPCs. - Production deployment hardening, including Prometheus Alertmanager or Grafana Telegram alerts. - Hiro, Satori, and Heroic Cloud. diff --git a/docs/setup/nakama-backend.md b/docs/setup/nakama-backend.md index c9315c4..84b415d 100644 --- a/docs/setup/nakama-backend.md +++ b/docs/setup/nakama-backend.md @@ -109,6 +109,18 @@ Copy-Item .env.example .env docker compose -f docker-compose.supabase.yml up --build ``` +Before any production deployment, set non-default values for: + +- `NAKAMA_CONSOLE_USERNAME` +- `NAKAMA_CONSOLE_PASSWORD` +- `NAKAMA_CONSOLE_SIGNING_KEY` +- `NAKAMA_SOCKET_SERVER_KEY` +- `NAKAMA_SESSION_ENCRYPTION_KEY` +- `NAKAMA_SESSION_REFRESH_ENCRYPTION_KEY` +- `NAKAMA_RUNTIME_HTTP_KEY` + +Local `admin` / `password` console credentials are allowed only for local dev. + ## Alerting Prometheus can scrape Nakama metrics from port `9100`. Telegram notifications should be handled by Prometheus Alertmanager or Grafana Alerting. Bot tokens and chat IDs must stay in local secrets, not in Git. From e3e268be59ef9d2eb27b1ff6b69e43224e10533e Mon Sep 17 00:00:00 2001 From: JOY Date: Fri, 15 May 2026 20:57:59 +0700 Subject: [PATCH 7/8] docs(backend): capture Nakama OSS comparison rationale --- docs/adr/0010-nakama-oss-game-backend.md | 28 +++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/adr/0010-nakama-oss-game-backend.md b/docs/adr/0010-nakama-oss-game-backend.md index e6ee82c..fedaa4f 100644 --- a/docs/adr/0010-nakama-oss-game-backend.md +++ b/docs/adr/0010-nakama-oss-game-backend.md @@ -58,7 +58,8 @@ Deferred: - Production secret rotation and non-default Nakama keys. The secret names are documented in `backend/nakama/.env.example`, but values are not committed. - Inventory, wallet, profile, quest, and SECOND token RPCs. - Production deployment hardening, including Prometheus Alertmanager or Grafana Telegram alerts. -- Hiro, Satori, and Heroic Cloud. +- Heroic Cloud migration after traction. +- Hiro and Satori. ## Alternatives considered @@ -94,6 +95,31 @@ Deferred: **Acceptance reason:** best balance of game-backend fit, ownership, and solo-founder maintainability. +## Managed backend comparison findings + +Pricing review date: 2026-05-15. Treat vendor prices as volatile and refresh before any production contract. + +The backend comparison intentionally separated **Nakama OSS** from **Nakama on Heroic Cloud**: + +- Nakama OSS is the current start point. Code, Docker Compose config, runtime modules, version pins, and local development flow stay in this repository. This is the best fit for AI coding agents because agents can inspect, edit, test, and reason about most of the game backend without relying on a vendor dashboard. +- Nakama on Heroic Cloud is a later managed migration path, not the current implementation shape. Heroic Cloud manages runtime infrastructure, database operations, backups, load balancers, metrics, and scaling. SECOND SPAWN can still keep custom server module source in the repo, but the cloud runtime and database operations are not repo-owned Docker infrastructure. + +Cost and fit notes: + +- **Nakama OSS:** lowest direct platform cost and strongest repo ownership. Main cost is operations effort: hosting, backups, monitoring, upgrades, and scale testing. +- **Nakama on Heroic Cloud:** observed minimum production tier is **$1,200/month** for Nakama. It has no DAU/MAU/CCU limit in the plan description, but capacity is still bounded by selected CPU/database resources. It does **not** include SECOND SPAWN's Unity/Fusion dedicated game servers. +- **Satori:** separate managed LiveOps product, observed minimum **$600/month** plus event-based ingestion pricing. It is not part of the MVP baseline. +- **PlayFab:** attractive free/startup path and broad Microsoft game-backend feature set, but production cost is usage-metered across events, profile reads/writes, statistics, Economy V2, lobby, matchmaking, Party, and Multiplayer Servers. For 100k MAU it can be cheap if calls are batched and sparse, or expensive if inventory/events/writes are frequent. Custom logic often moves into PlayFab/Azure execution paths, which is less transparent for AI agents than repo-owned Nakama modules. +- **AccelByte AGS:** strongest turnkey studio platform among the managed options, including online services and optional AccelByte Multiplayer Servers. Public estimates for 100k MAU show roughly **$1,689/month** for Multiplayer or **$2,420/month** for Complete, but AccelByte bills on daily PCCU, not MAU. For a long-session MMO/ARPG, real PCCU can make cost materially higher. This is a later studio-scale option, not a solo-founder MVP default. + +Decision implication: + +- Start with **Nakama OSS** to preserve open-source credibility, repo-level control, and AI-agent operability. +- Do **not** start with Heroic Cloud just because it is managed. Use it after traction when operations risk costs more than the monthly platform fee. +- Do **not** add Satori until SECOND SPAWN has real LiveOps needs. +- Revisit PlayFab only if Microsoft/Xbox platform benefits become strategically important enough to accept lock-in and usage-meter risk. +- Revisit AccelByte only if the project has studio-scale budget, launch pressure, and a need for turnkey backend operations. + ## Consequences ### Positive From e73066f3039d08f6512690ef0a387cb877eb1ae0 Mon Sep 17 00:00:00 2001 From: JOY Date: Fri, 15 May 2026 21:01:49 +0700 Subject: [PATCH 8/8] chore(backend): scope gateway CI and clean Nakama runtime --- .github/workflows/backend-test.yml | 11 +++++++---- backend/nakama/modules/src/main.ts | 12 +++++------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/backend-test.yml b/.github/workflows/backend-test.yml index e011872..dee4936 100644 --- a/.github/workflows/backend-test.yml +++ b/.github/workflows/backend-test.yml @@ -4,11 +4,11 @@ on: push: branches: [main] paths: - - 'backend/**' + - 'backend/gateway/**' - '.github/workflows/backend-test.yml' pull_request: paths: - - 'backend/**' + - 'backend/gateway/**' - '.github/workflows/backend-test.yml' jobs: @@ -26,12 +26,15 @@ jobs: with: go-version: '1.23' cache: true - cache-dependency-path: backend/gateway/go.sum + cache-dependency-path: backend/gateway/go.mod - name: Verify go.mod is tidy run: | go mod tidy - git diff --exit-code go.mod go.sum + if [ -n "$(git status --porcelain -- go.mod go.sum)" ]; then + git status --short -- go.mod go.sum + exit 1 + fi - name: Vet run: go vet ./... diff --git a/backend/nakama/modules/src/main.ts b/backend/nakama/modules/src/main.ts index ed64261..d2e8389 100644 --- a/backend/nakama/modules/src/main.ts +++ b/backend/nakama/modules/src/main.ts @@ -1,16 +1,16 @@ const rpcIdHealth = "secondspawn_health"; -let InitModule: nkruntime.InitModule = function ( +function InitModule( ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, initializer: nkruntime.Initializer -) { +): void { initializer.registerRpc(rpcIdHealth, rpcHealth); logger.info("Second Spawn Nakama runtime loaded."); -}; +} -const rpcHealth: nkruntime.RpcFunction = function ( +function rpcHealth( ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, @@ -21,6 +21,4 @@ const rpcHealth: nkruntime.RpcFunction = function ( service: "second-spawn-nakama", userId: ctx.userId || null }); -}; - -InitModule.bind(null); +}