Skip to content

Running the Diplomacy Server Locally

Install, configure, and run the API server plus the optional components (browser frontend, Telegram bot). All commands assume you are in the repository root with the venv active.

Prerequisites

  • Python 3.14 (pinned in pyproject.toml)
  • PostgreSQL — required by the API and most tests
  • libcairo2 — required by CairoSVG for map rendering
  • Node.js 18+ / npm — only for the browser frontend
  • A Telegram bot token from @BotFather — only for the bot

One script installs everything on Arch, Debian/Ubuntu, or macOS with Homebrew:

./install_prerequisites.sh

Or by hand:

# Arch
sudo pacman -S python python-pip postgresql nodejs npm cairo
sudo systemctl start postgresql

# Debian / Ubuntu (Python 3.14 via deadsnakes on older releases)
sudo add-apt-repository -y ppa:deadsnakes/ppa && sudo apt-get update
sudo apt-get install -y python3.14 python3.14-venv python3-pip postgresql postgresql-client libcairo2 libcairo2-dev
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt-get install -y nodejs
sudo systemctl start postgresql

# macOS
brew install python@3.14 postgresql@16 node@20 cairo
brew services start postgresql@16

1. Virtual environment and packages

python3.14 -m venv venv
source venv/bin/activate          # Windows: venv\Scripts\activate
pip install --upgrade pip
pip install -r requirements.txt

2. Database

The default connection URL is postgresql+psycopg2://diplomacy_user:password@localhost:5432/diplomacy_db.

The quickest path creates the role, the database, and the schema in one go:

./setup_database.sh

To do it manually:

sudo -u postgres psql
CREATE USER diplomacy_user WITH PASSWORD 'password';
CREATE DATABASE diplomacy_db OWNER diplomacy_user;
GRANT ALL PRIVILEGES ON DATABASE diplomacy_db TO diplomacy_user;
\q
psql -U diplomacy_user -h localhost -d diplomacy_db   # verify the connection
alembic upgrade head                                  # create/update tables

To use different credentials, set SQLALCHEMY_DATABASE_URL (see step 3).

Game IDs start high. The displayed game_id is the games.id primary key, and the Postgres sequence advances on every insert including tests and rolled-back transactions. To restart numbering in development — only when games is empty or you're happy to wipe it:

TRUNCATE games CASCADE;
ALTER SEQUENCE games_id_seq RESTART WITH 1;

3. Environment variables

Everything has a working default. To avoid exporting variables each session, put them in .env in the repository root (loaded via python-dotenv; never commit secrets).

Variable Purpose
SQLALCHEMY_DATABASE_URL PostgreSQL connection URL
DIPLOMACY_JWT_SECRET JWT signing secret — must be set in production
TELEGRAM_BOT_TOKEN Telegram bot token (bot process only)
DIPLOMACY_API_URL API base URL used by the bot (default http://localhost:8000)
DIPLOMACY_BOT_SECRET Shared secret between bot and API; also arms Idempotency-Key replay and the /bot/outbox endpoints
DIPLOMACY_BOT_DATA_DIR Where the bot keeps its durable queue (outbox.sqlite3; default bot_data/, git-ignored)
DIPLOMACY_NOTIFY_POLL_SECONDS / DIPLOMACY_OUTBOX_POLL_SECONDS Bot poll intervals for pulling notifications / replaying its queue (defaults 3 / 5)
DIPLOMACY_CORS_ORIGINS Allowed CORS origins (default *; restrict in production)
DIPLOMACY_MAP_PATH Path to the map SVG (default maps/standard.svg)
DIPLOMACY_LOG_LEVEL / DIPLOMACY_LOG_FILE Log level (default INFO); log to a file instead of stdout
DIPLOMACY_PASSWORD_RESET_BASE_URL Base URL for password-reset links (e.g. http://localhost:5173)
DIPLOMACY_DEV_SHOW_RESET_LINK 1 returns the reset link in the response (development only)
DIPLOMACY_SMTP_HOST / _PORT / _USE_TLS / _USER / _PASSWORD / _FROM / _FROM_NAME SMTP settings; if HOST is set, forgot-password sends real email

4. Run the API server

PYTHONPATH=src uvicorn server._api_module:app --host 0.0.0.0 --port 8000 --reload

PYTHONPATH=src is required — the server and engine packages live under src/. Drop --reload when debugging startup issues.

  • API: http://localhost:8000
  • Swagger UI: http://localhost:8000/docs
  • Verify: curl http://localhost:8000/health

5. Run the browser frontend (optional)

In a second terminal:

cd frontend && npm install && npm run dev

The app runs at http://localhost:5173; Vite proxies API routes to the backend. Set VITE_API_URL in frontend/.env if the API is elsewhere. See BROWSER_CLIENT.md and frontend/README.md.

6. Run the Telegram bot (optional)

export TELEGRAM_BOT_TOKEN=your-token-from-BotFather
PYTHONPATH=src python -m server.telegram_bot

The bot starts whether or not the API is up. With the API down, reads answer with a clear "server unreachable" message and writes (orders, messages) are queued in bot_data/outbox.sqlite3 and delivered once the API answers — a convenient way to exercise the queue locally is to stop uvicorn, send /order A PAR H, check /queue, then start it again. Production runs the bot on a separate host; see DEPLOYMENT.md.

Commands: TELEGRAM_BOT_COMMANDS.md.

(A minimal Discord bot exists at src/server/discord_bot/ — set DIPLOMACY_DISCORD_BOT_TOKEN and run PYTHONPATH=src python -m server.run_discord_bot. It is out of scope for the current roadmap and is kept only for backward compatibility.)

7. Run the tests

pytest tests/ -v
pytest tests/ --cov=src --cov-report=term-missing

Database-dependent tests skip silently without SQLALCHEMY_DATABASE_URL (or DIPLOMACY_DATABASE_URL) set — a run without a database looks falsely green. Testing strategy: specs/testing_and_validation.md.

Quick reference

Task Command
API server PYTHONPATH=src uvicorn server._api_module:app --host 0.0.0.0 --port 8000 --reload
Migrations alembic upgrade head
Tests pytest tests/ -v
Lint ruff check src/
Frontend dev cd frontend && npm run dev
Telegram bot PYTHONPATH=src python -m server.telegram_bot
Demo game python examples/demo_perfect_game.py

Troubleshooting

ModuleNotFoundError: server / engine — run from the repository root with PYTHONPATH=src.

Database connection errors — check that PostgreSQL is running (pg_isready), that the role and database exist, and that SQLALCHEMY_DATABASE_URL matches. Test directly with psql -U diplomacy_user -h localhost -d diplomacy_db. If columns are missing, run alembic upgrade head.

401 on /games/.../join or /auth/refresh — the access or refresh token is invalid or expired. Changing DIPLOMACY_JWT_SECRET invalidates every existing token; log in again.

CORS errors from the frontend — set DIPLOMACY_CORS_ORIGINS (e.g. http://localhost:5173).

"Order failed" — check the syntax, that the order type matches the current phase, that the unit exists and belongs to your power, and that province names are valid. The canonical list of what's legal right now is GET /games/{id}/legal_orders/{power}.

Map generation is slow the first time — maps are cached in memory and at /tmp/diplomacy_map_cache; subsequent requests are fast. If CairoSVG fails to import, install libcairo2.

Bot doesn't respond — confirm TELEGRAM_BOT_TOKEN is set, the bot process is running, the API is reachable at DIPLOMACY_API_URL, and that you have sent /register.

Tests fail or skip unexpectedly — check the database URL first (see above), then re-run the single test with pytest tests/test_file.py::test_name -v.

For production troubleshooting, see the Troubleshooting section of docs/DEPLOYMENT.md.