Architecture
The monorepo splits into a small number of independent deployables that communicate over HTTP, gRPC, and WebSockets. This page lists each service, what it owns, and traces a single request end-to-end.
Service map
Section titled “Service map”| Service | Path | Port(s) | Role |
|---|---|---|---|
| Engine libraries | packages/weiqi-engine, packages/go-engine, packages/katago-server | — | Pure rules / move-generation logic, no I/O. |
| Gateway | apps/weiqi-server | 3002 | NestJS API: game, bot, analysis, challenges, accounts, agent. |
| KataGo wrapper | apps/katago-service | 4011 HTTP / 50051 gRPC | Spawns the KataGo binary, fans out requests via BullMQ. |
| Board photo OCR | apps/recognition-service | 4010 | Runs the moku-v3 ONNX model, sync and async endpoints. |
| MCP server | apps/mcp-bridge | 3003 | Exposes 6 tools (list-levels, start-bot-game, …) over HTTP / stdio. |
| Auth microservice | apps/auth-app | 3004 | Better-Auth + Drizzle, OIDC, magic-link, sessions. |
| USDC indexer | apps/billing-indexer | 9090 | Watches USDC.Transfer events, delivers signed webhooks to auth-app. |
| Player UI | apps/game-web | 3001 | React 19 + Vite SPA: play, bots, analysis, scan, profile, bot-ladder. |
| Marketing / blog / landing | apps/main-app | 3003 | Payload CMS on Next.js 16 (OpenNext → Cloudflare). |
| Astro landing (legacy) | apps/main-app-astro | 3001 | Astro 5 marketing site + blog, en/ja/zh/ko. |
| Admin console | apps/admin-app | 3014 | Internal admin UI, gated by VITE_ADMIN_REQUIRED_ROLE. |
| Community | apps/comunity-app | — | Community features (separate frontend). |
System diagram
Section titled “System diagram”flowchart LR subgraph Client GW[game-web<br/>React 19 + Vite] MAA[main-app<br/>Payload CMS] DOCS[weiqi-docs<br/>Astro + Starlight] end
subgraph Edge WS[weiqi-server<br/>NestJS :3002] AUTH[auth-app<br/>Better-Auth :3004] MCP[mcp-bridge<br/>MCP :3003] end
subgraph Workers KATA[katago-service<br/>HTTP :4011 / gRPC :50051] REC[recognition-service<br/>ONNX moku-v3 :4010] BILL[billing-indexer<br/>USDC indexer :9090] end
subgraph Data PG[(Postgres)] RD[(Redis)] S3[(S3 / MinIO)] USDC[(USDC on-chain)] end
GW -->|REST + Socket.IO| WS GW -->|session cookie| AUTH MAA -->|session| AUTH MCP -->|REST| WS WS -->|gRPC| KATA WS -->|HTTP| REC BILL -->|Ed25519 webhook| AUTH WS --> PG WS --> RD WS --> S3 AUTH --> PG BILL --> USDCRequest flow: bot game move
Section titled “Request flow: bot game move”A player presses “play move” in game-web. The request travels through five
hops before KataGo returns a chosen move.
- Browser → gateway.
apps/game-web/src/entities/bot-game/api/bot-game.api.tsPOSTs to/api/game/bot/games/:id/moveviacreateApiInstance(rawfetch, session cookie auto-attached). The gateway is NestJS atapps/weiqi-server/src/main.ts:14. - Auth guard.
AuthGuardreadsbetter-auth.session_token(the__Secure-prefix is tried first), callsAccountsService.validateSession, attaches{user, session}to the request. - BotGameService.
apps/weiqi-server/src/modules/bot/application/bot-game.service.tsvalidates the move againstBotGameService’s authoritative board state and appends it to thebotGameMovesrow. - KatagoService.
KatagoService.getMovepicks anIKatagoClientbased on configuration:GrpcKatagoClient(default),HttpKatagoClient, the in-processBullMqKatagoClient, orMockKataGoClientfor tests. - kata-go binary.
KataGoBinaryEngineAdapter.analyzeshells out to the KataGo process, streams JSON from stdout, parses the GTP response, and returns{moveInfos, rootInfo, ownership?}. - Persist + emit. The move is written to
botGameMoves, the bot’s reply is appended, andBotRealtimeEmitterpushesgameStateandbotMoveto the player’s socket room. The browser reconciles without a refresh.
For an end-to-end analysis after the game ends, the same path is used but
with analyzeTurns: [0..N] so KataGo replays the whole game in a single
query — see KataGo Review.
Boundaries
Section titled “Boundaries”- Engine packages have no I/O.
packages/weiqi-engineandpackages/go-engineare pure TypeScript. They never read env vars, never hit the network, never touch the DB. Everything they need is passed in via constructor arguments or method parameters. - The gateway is the only writer to game state. Bot moves, hint requests,
review snapshots, ranking updates — all flow through
weiqi-servermodules.apps/katago-serviceis stateless and only sees the request payload. billing-indexerdoes not share a database withauth-app. They communicate exclusively through signed webhooks (Ed25519, verified viapackages/billing-shared/webhook-signature.ts). This keeps the indexer free to redeploy without touching auth.- Auth is centralised. Every internal HTTP call to
weiqi-servercarries the user’s session cookie, which is validated byaccountsmodule againstauth-app. There is no service-to-service JWT — only session cookies.
Where to look next
Section titled “Where to look next”- Engine internals: weiqi-engine, go-engine.
- Service deep-dives: weiqi-server, katago-service, mcp-bridge, billing-indexer, recognition-service.
- Feature flows: Bot Ladder, KataGo Review.