katago-service
apps/katago-service is a thin NestJS service whose only job is to spawn the
KataGo binary, serialise requests to its GTP-shaped stdin/stdout JSON, and
return structured responses. It exposes two transports so the gateway can
talk to it however it prefers, and a BullMQ queue so multiple weiqi-server
instances can share a single KataGo budget.
Transports
Section titled “Transports”HTTP — port 4011
Section titled “HTTP — port 4011”KataGoHttpController (src/katago/katago-http.controller.ts:17) accepts:
POST /v1/analysis?priority=1Content-Type: application/json
{ "id": "...", "analyzeOptions": { ... } }priority is optional; it overrides the default worker priority
(DEFAULT_ANALYSIS_PRIORITY=1) for this request.
gRPC — port 50051
Section titled “gRPC — port 50051”Proto at apps/katago-service/proto/katago.proto, package katago.v1,
service KataGoService (src/katago/katago-grpc.controller.ts:6). Methods:
| RPC | Use case |
|---|---|
AnalyzeSync | Synchronous: returns when KataGo finishes. |
AnalyzeAsync | Fire-and-forget: returns a jobId, fetch result via GetResult. |
GetJob | Query current state of a job. |
GetResult | Fetch a finished job’s result. |
GetHealth | Liveness + active job count. |
GetVersion | KataGo binary version + git hash. |
analyze_options arrives as a JSON string in proto. The gateway sends the
canonical analyzeTurns array here for full-game review.
Engine adapters
Section titled “Engine adapters”adapters/engine/:
KataGoBinaryEngineAdapter(katago-binary.engine.ts:155) — spawns the KataGo binary (KATAGO_PATH, defaultkatago), pipes JSON requests on stdin, parses JSON lines on stdout. Caches the process handle per worker.MockKataGoEngine— deterministic test double.
The active adapter is chosen at boot. There is no A/B — pick by env.
Queueing and concurrency
Section titled “Queueing and concurrency”BullMQ queue katago-analysis decouples weiqi-server from KataGo’s actual
throughput. WeiQiServerKatagoProcessor
(adapters/queue/weiqiserver-katago.processor.ts:89) consumes jobs and shares
a PrioritySemaphoreKataGoConcurrencyGuard
(adapters/concurrency/priority-semaphore.guard.ts) so the configured caps
are global across all workers.
Env knobs:
KATAGO_MAX_CONCURRENT— max simultaneous KataGo processes.KATAGO_RATE_LIMIT_MAX— rate-limit cap.KATAGO_RATE_LIMIT_DURATION_MS— rate-limit window.
Worker priority is set per-job (priority field). Lower number = higher
priority (BullMQ convention).
Environment
Section titled “Environment”PORT=4011GRPC_PORT=50051KATAGO_PATH=katago # binary on $PATHKATAGO_MODEL_PATH= # absolute path to .bin.gz model fileKATAGO_CONFIG_PATH= # absolute path to GTP configQUEUE_MODE=none # "bullmq" to enable queueingREDIS_URL=redis://localhost:6379QUEUE_MODE=none makes the service answer directly. Set to bullmq once
you have multiple weiqi-server replicas or want to share capacity.
Local dev
Section titled “Local dev”cp apps/katago-service/.env.example apps/katago-service/.env# Install the KataGo binary and place a model file somewhere on disk.# Then update KATAGO_PATH / KATAGO_MODEL_PATH / KATAGO_CONFIG_PATH.pnpm --dir apps/katago-service devSmoke test:
curl -s -X POST http://localhost:4011/v1/analysis \ -H 'content-type: application/json' \ -d '{"id":"smoke","analyzeOptions":{"boardXSize":19,"boardYSize":19,"rules":"chinese","analyzeTurns":[0]}}'Why this layer exists
Section titled “Why this layer exists”weiqi-server does not shell out to KataGo directly for two reasons:
- Process isolation. A crashing KataGo process takes the worker down but not the gateway.
- Capacity sharing. When
weiqi-serverscales horizontally, every replica can post jobs to the same queue and a singlekatago-serviceinstance (or pool) drains them under one concurrency cap.
See KataGo Review for how the queue is used in the unified full-game review flow.