Multi-Regional Architecture¶
SectorWars 2102 supports a hub-and-spoke topology where a Central Nexus federates many player-owned regional servers. Each region is its own database, container, and governance domain. Travel and diplomacy between regions are first-class game mechanics.
This document focuses on the runtime / service-level picture. The data model details live in DATA_MODELS; this file connects those tables to the actual services.
Region kinds¶
Defined as a Postgres enum on region.region_type (services/gameserver/src/models/region.py):
| Type | Sectors | Notes |
|---|---|---|
central_nexus |
exactly 5000 | The hub. Single instance. |
terran_space |
exactly 300 | The fixed starter region for new players. |
player_owned |
100–1000 | Variable, owner-configurable, paid (PayPal subscriptions). |
These constraints are enforced by a CHECK constraint on the regions table (see lines 139-141 of services/gameserver/src/models/region.py).
The Central Nexus uses a cluster-based geographic model. services/gameserver/src/api/routes/nexus.py exposes clusters via ClusterInfoResponse as the primary geographic grouping inside the Nexus.
Services involved¶
gameserver / central-nexus-server¶
Same Docker image (services/gameserver), two roles distinguished by env:
- gameserver (dev profile) — single instance pointing at the dev Postgres.
- central-nexus-server (multi-regional / production profiles) —
SERVICE_TYPE=central-nexus,REGION_ID=central-nexus,NEXUS_ADMIN_MODE=true,ENABLE_CROSS_REGIONAL_API=true. Connects tocentral-nexus-dbandredis-nexus.
Multi-regional API surface lives in:
services/gameserver/src/api/routes/nexus.py(prefix/nexus) — generate Nexus, view stats, list clusters.services/gameserver/src/api/routes/regional_governance.py(prefix/regions) — economic / governance config, policies, elections, treaties.services/gameserver/src/services/nexus_generation_service.pyandregional_auth_service.py— domain logic.
regional-server-template¶
Defined in docker-compose.yml under the regional-template profile but not started as part of a normal docker compose up. It is a recipe — region-manager clones it for each provisioned region. Key env at runtime:
SERVICE_TYPE=regional-server.REGION_ID=${REGION_NAME}.CENTRAL_NEXUS_URL=http://central-nexus-server:8080.REGIONAL_ISOLATION=true.- Connects to its own Postgres (
{REGION_NAME}-db) and sharesredis-nexus(DB index1).
region-manager¶
Standalone FastAPI service that orchestrates region lifecycle.
- Source:
services/region-manager/src/main.py,region_provisioner.py,monitoring.py,models.py,config.py. - Templates:
services/region-manager/templates/docker-compose.region.yml.j2— Jinja-rendered Compose snippet for a new region. - Talks directly to the Docker daemon via the mounted socket (
/var/run/docker.sock) — it candocker runsiblings.
Its endpoints:
| Method | Path | Effect |
|---|---|---|
POST |
/regions/provision |
Validate the request, allocate DB, render compose template, start container, register with the Nexus. Returns immediately; the heavy lift is a background task. |
DELETE |
/regions/{name} |
Stop + remove containers, drop the regional DB, unregister from Nexus. |
POST |
/regions/{name}/scale |
Adjust CPU / memory / disk on a running region. |
GET |
/regions, /regions/{name} |
List / inspect tracked regions. |
GET |
/metrics |
Aggregate region count, player count, CPU/memory totals. |
GET |
/health |
Used by the compose healthcheck. |
A 30-second background loop (monitor_regions_loop) polls Docker stats per region. When a region averages >80 % CPU or >85 % memory it auto-scales up — capped at 8 cores / 16 GB. Symmetric scale-down kicks in when CPU stays under 20 % and memory under 30 % for the configured cool-down window.
redis-nexus¶
Dedicated Redis instance on port 6380 for cross-region pub/sub and shared cache. Lives in the nexus-network. The dev profile's redis-cache is not the same instance — production deployments run both.
nginx-gateway routing¶
services/nginx-gateway/nginx.conf knows about regional fan-out. Any path matching /api/v1/regions/{region_name}/... is rewritten and proxied to region-{region_name}-server:8080. This is how the player client and admin UI reach a specific regional server without each frontend having to know its URL.
Provisioning flow (verified)¶
- Admin or paid player calls
POST {region-manager}/regions/provisionwith name, owner ID, sector count, etc. region-manager:- Validates the request (
provisioner.validate_region_request). - Marks the region
provisioningin its in-memory map. - Schedules a background task that:
- Creates a new Postgres database for the region.
- Generates a region config (
generate_region_config). - Renders the Jinja Compose template at
templates/docker-compose.region.yml.j2. - Starts the regional container(s) via the Docker SDK.
- Calls
register_with_nexusso the Central Nexus knows about it.
- The new regional gameserver boots, connects to its DB and to
redis-nexus, and starts answering API calls. - Players reach it via
nginx-gatewayat/api/v1/regions/{name}/....
Termination is the inverse: DELETE /regions/{name} revokes containers, drops the DB, and unregisters from the Nexus.
Travel between regions¶
Modeled on inter_regional_travels (see services/gameserver/src/models/region.py):
- Source region, destination region, travel method (
platform_gate,player_gate,warp_jumper). assets_transferredJSONB — what the player carries with them.- Status workflow
in_transit → completed | failed | cancelled. - Constraints: source ≠ destination; cost ≥ 0.
The runtime path uses Redis pub/sub on redis-nexus to coordinate handoffs between source and destination regional servers; the relevant logic is split between regional_governance.py and the regional auth service.
Governance and diplomacy¶
Each player-owned region picks a governance_type (autocracy | democracy | council), a voting_threshold (0.1–0.9), and an election_frequency_days (30–365). These show up as columns on the regions table.
regional_policies— proposed changes (tax rate, PvP rules, trade policy, immigration, defense, cultural). Voted on by region members; outcome captured invotes_for/votes_against/status.regional_elections+regional_votes— elections forgovernor,council_member,ambassador,trade_commissioner. Vote weight is0.0–5.0.regional_treaties— bilateral agreements (trade_agreement,defense_pact,non_aggression,cultural_exchange).
Membership is per-player per-region (regional_memberships): visitor | resident | citizen, with reputation scores -1000…1000 and voting power 0.0…5.0.
These tables and their constraints all live in services/gameserver/src/models/region.py; the API endpoints are in services/gameserver/src/api/routes/regional_governance.py (prefix /regions).
Validations¶
Numeric bounds (tax rate, voting threshold, election frequency, etc.) are enforced as SQL CHECK constraints in services/gameserver/src/models/region.py and as Pydantic Field(ge=..., le=...) validators in services/gameserver/src/api/routes/regional_governance.py (e.g. EconomicConfigUpdate, GovernanceConfigUpdate).
Subscription / billing¶
Region ownership ties into PayPal subscriptions. central-nexus-server carries the relevant env vars (PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PAYPAL_GALACTIC_CITIZEN_PLAN_ID, PAYPAL_REGIONAL_OWNER_PLAN_ID, PAYPAL_WEBHOOK_ID, PAYPAL_MODE). The relevant route file is services/gameserver/src/api/routes/paypal.py.
What to read next¶
- services.md — service-level details on
region-manager,central-nexus-server,nginx-gateway. - deployment.md — profile / network / volume reference.
- auth.md — how regional auth ties back to the Nexus user store.
Sectorwars2102/services/gameserver/src/models/region.py— the canonical schema for everything regional.