Table of Contents
I wanted a Go bot that plays games on KGS while I sleep. Not to grief anyone — just to have a persistent presence on the server, accumulate games, and watch a machine play the oldest board game in the world against real humans, all day, every day.
The result: a Python wrapper around KataGo and kgsGtp.jar, running on a $5/month Lightsail instance, playing Chinese rules on 19x19, saving every game as an SGF file, and reporting stats back to me. Here’s how it works.
The Architecture
The key insight that makes this project simple: you don’t need to implement the KGS protocol yourself. KGS provides kgsGtp.jar, an official Java client that bridges their proprietary server protocol to the standard Go Text Protocol (GTP). Any GTP-speaking engine — GnuGo, Leela Zero, KataGo — can plug right in.
KGS Server <──KGS Protocol──> kgsGtp.jar <──GTP stdin/stdout──> KataGo
^
│
Python Wrapper
(process management,
SGF post-processing,
statistics)
The Python wrapper doesn’t sit in the communication path at all. kgsGtp.jar handles authentication, game acceptance, move relay, and reconnection. Our code just:
- Generates the kgsGtp.jar config file from our YAML config
- Launches and monitors the Java process
- Watches for new SGF files and renames/logs them
- Provides a stats CLI
Understanding kgsGtp.jar
kgsGtp.jar is a Java application that KGS maintains for bot authors. You configure it with an .ini file that specifies your engine command, KGS credentials, and game preferences:
engine=/usr/local/bin/katago gtp -model /opt/models/kata.bin.gz -config /opt/katago_gtp.cfg
name=MyKataGoBot
password=secret
room=Computer Go
mode=custom
reconnect=true
rules=chinese
boardSize=19
When you run java -jar kgsGtp.jar config.ini, it:
- Connects to KGS and authenticates
- Joins the specified room
- Launches your engine command as a subprocess
- Relays challenges, moves, and time controls between KGS and the engine via GTP
- Saves completed games as SGF files (if configured)
- Automatically reconnects if the connection drops
This means our bot is really just KataGo with some configuration and process management on top. The heavy lifting — both the Go AI and the server protocol — is handled by existing, battle-tested software.
KataGo and GTP
KataGo is currently the strongest open-source Go engine. It speaks GTP, which is a simple text protocol:
= (ready)
boardsize 19
=
clear_board
=
komi 7.5
=
play B D4
=
genmove W
= E3
The engine receives board state updates and responds with moves. Time management, scoring, and resignation are all handled through GTP commands that kgsGtp.jar sends automatically.
For model selection, I use kata1-b18c384nbt — an 18-block model that balances strength (solid dan level on KGS) with speed (runs well on a small cloud instance without a GPU). You can use larger models if you have a GPU instance, but for a $5/month nano instance running CPU-only, the 18-block model is the sweet spot.
The Python Wrapper
The wrapper is a small Python package with five modules:
Configuration (config.py)
We use a YAML config file because it’s human-friendly, then translate it to kgsGtp.jar’s .ini format:
def generate_kgsgtp_config(bot_config: BotConfig, output_path: str) -> None:
engine_command = (
f"{bot_config.katago_binary} gtp "
f"-model {bot_config.katago_model} "
f"-config {bot_config.katago_config}"
)
lines = [
f"engine={engine_command}",
f"name={bot_config.kgs_username}",
f"password={bot_config.kgs_password}",
f"room={bot_config.kgs_room}",
"mode=custom",
"reconnect=true",
"rules=chinese",
"boardSize=19",
]
Path(output_path).write_text("\n".join(lines) + "\n")
Process Management (runner.py)
The runner launches kgsGtp.jar and restarts it with exponential backoff if it crashes:
def compute_backoff(attempt: int) -> float:
"""min(5 * 2^(attempt-1), 300) seconds."""
if attempt <= 0:
return 5.0
return min(5.0 * (2 ** (attempt - 1)), 300.0)
This gives us: 5s, 10s, 20s, 40s, 80s, 160s, 300s (cap). If KGS is down for maintenance, the bot waits patiently and reconnects when it comes back.
SGF Watcher (sgf_watcher.py)
A background thread polls the SGF directory every 10 seconds. When a new file appears:
- Parse it to extract opponent name, result, move count
- Rename to
2026-08-21_143022_OpponentName.sgf - Log a game summary
- Optionally sync to S3
Stats Engine (stats.py)
A CLI tool that reads all saved SGFs and reports:
=== KGS KataGo Bot Statistics ===
Total games: 142
Wins: 98 | Losses: 44
Win rate: 69.0%
By color:
As Black: 52W / 20L
As White: 46W / 24L
AWS Deployment
The whole thing runs on a Lightsail nano_3_0 instance — 512MB RAM, 2 vCPU burst, Debian 12. Costs $5/month.
Systemd Service
[Service]
Type=simple
User=kgsbot
ExecStart=/usr/bin/python3 -m kgs_katago_bot.runner --config /opt/kgs-katago-bot/config.yaml
Restart=always
RestartSec=10
Between systemd’s Restart=always and our internal exponential backoff, the bot is extremely resilient. Systemd handles process-level crashes; our wrapper handles kgsGtp.jar/KGS connection issues.
Deployment Script
A single deploy.sh handles first-time setup: install packages, create the bot user, download KataGo + model + kgsGtp.jar, configure systemd, and start the service. From a fresh instance to playing games in under 10 minutes (mostly waiting for the 1.5GB model download).
Cost Breakdown
| Item | Monthly Cost |
|---|---|
| Lightsail nano instance | $5.00 |
| S3 for game backup | ~$0.01 |
| Total | ~$5/month |
KGS Bot Policies
A few things to know about running a bot on KGS:
- Bots play in the Computer Go room — that’s the convention
- You need to email
admin@gokgs.comto get your account flagged as a bot for ranked play - Bots must follow the KGS Terms of Service — no cheating, no advertising, play honestly
- kgsGtp.jar is the officially supported way to connect bot engines
- Chinese rules are standard for KGS bots
- Only 19x19 boards are ranked
Lessons Learned
kgsGtp.jar config is fiddly. The documentation is sparse and some options aren’t obvious. The mode=custom setting is crucial — without it, the bot doesn’t accept challenges correctly. I spent time reading old forum posts from 2005-era bot authors to figure out the right incantation.
KataGo on CPU is still strong. Even without a GPU, the 18-block model on 2 threads plays at a solid dan level. The limiting factor is visits per move — at 800 visits on CPU, it thinks for a few seconds per move, which is perfectly fine for KGS time controls.
The SGF file appearance timing is unpredictable. kgsGtp.jar writes the SGF file when the game ends, but there can be a delay. Polling every 10 seconds catches everything reliably without wasting CPU.
Exponential backoff is essential. KGS has occasional maintenance windows. Without backoff, the bot would hammer the server with reconnection attempts and potentially get rate-limited or banned.
Source code: kgs-katago-bot on GitHub