This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
SyncServer is a FastAPI/Uvicorn HTTP+WebSocket server for synchronizing Minecraft schematics across game servers. Clients are grouped into named pools (e.g. practice, build-team); each pool has its own isolated schematic storage, WebSocket broadcast channel, and backup stream.
pip install -r requirements.txt
python app.pyNo build step. No test suite. Configuration is read from conf.txt (copy from conf.txt.example), or from $SCHEMSYNC_CONF.
conf.txt (key=value, inline # comments stripped):
| Key | Default | Env override |
|---|---|---|
host |
0.0.0.0 |
SCHEMSYNC_HOST |
port |
8756 |
SCHEMSYNC_PORT |
api_key |
change-me-in-production |
SCHEMSYNC_API_KEY |
data_dir |
data (relative to app.py) |
SCHEMSYNC_DATA |
backup_hours |
12.0 |
SCHEMSYNC_BACKUP_HOURS |
The entire application lives in app.py — one file, no packages.
Startup flow: load_settings() → create SCHEMATICS_ROOT/BACKUPS_ROOT → register FastAPI routes → launch backup_loop() via @app.on_event("startup").
Pool model: All /v1/ endpoints are prefixed with /{pool}. Pools are auto-created on first authenticated use (pool_schematics_dir() calls mkdir(exist_ok=True)). Pool names are validated by _POOL_RE = r"^[A-Za-z0-9_-]{1,64}$" — reject anything else with 400.
Storage layout:
data/schematics/<pool>/... ← schematic files
data/backups/<pool>/... ← zip backups
Auth: Single master api_key. HTTP endpoints use X-API-Key header via the require_api_key FastAPI dependency. WebSocket uses ?token= query param. Pool creation is implicitly gated — no unauthenticated request reaches pool-touching code.
WebSocket broadcast: _ws_pools: dict[str, set[WebSocket]] — one set per pool. _broadcast(pool, msg) fans out to that pool's set and silently prunes dead connections. broadcast_schematic(pool, path) and broadcast_repo_resync(pool) are the two public helpers.
Path security: safe_relative_path() rejects .. and leading /. pool_schematics_dir() / pool_backups_dir() resolve and create the per-pool directory.
File mtime preservation: Upload accepts X-Client-Mtime-Ms (ms since epoch) → os.utime. Download returns X-Mtime-Ms header.
Backup loop: Sleeps first, then iterates every directory under SCHEMATICS_ROOT, zipping each pool independently to data/backups/<pool>/schematics-YYYYMMDD-HHMMSS.zip. Minimum 1 h enforced.
| Method | Path | Notes |
|---|---|---|
| GET | /health |
Returns config path + list of existing pools; no auth |
| GET | /v1/{pool}/list |
JSON array of {path, mtime_ms, size} |
| GET | /v1/{pool}/file/{path} |
File download; X-Mtime-Ms response header |
| POST | /v1/{pool}/upload |
Multipart: path field + file field |
| POST | /v1/{pool}/notify/repo-resync |
Broadcasts repo_resync to pool |
| POST | /v1/{pool}/hook/plugin |
JSON body {path}, broadcasts schematic_updated to pool |
| WS | /v1/{pool}/ws |
?token=<api_key>; scoped to pool |