Shared Configuration
Problem
Game configuration (body hierarchy, spin axes, ship classes) is duplicated
between the Python backend (galaxy_config/__init__.py) and the JavaScript
frontend (bodyConfig.js, shipSpecsData.js). Manual synchronization is
error-prone — adding a new ship class or body on one side but not the other
causes silent UI failures.
Solution: Single JSON Source of Truth
A single shared_data.json file defines the three shared data structures.
Both Python and JavaScript consume it directly, eliminating duplication.
Data File
Location: services/shared/galaxy_config/shared_data.json
Contains three top-level keys:
| Key | Type | Description |
|---|---|---|
BODY_PARENTS |
object<string, string> |
Moon name → parent planet name (20 entries) |
BODY_SPIN_AXES |
object<string, [number, number, number]> |
Body name → ecliptic spin axis unit vector (10 entries) |
SHIP_CLASSES |
object<string, object> |
Ship class key → physics properties |
All values are JSON-safe primitives (strings, numbers, arrays, objects). No Python-specific syntax (underscored numeric literals, tuples, etc.).
Python Consumption
galaxy_config/__init__.py loads the JSON at import time:
import json, os
_data_path = os.path.join(os.path.dirname(__file__), 'shared_data.json')
with open(_data_path) as _f:
_shared = json.load(_f)
BODY_PARENTS = _shared['BODY_PARENTS']
BODY_SPIN_AXES = _shared['BODY_SPIN_AXES']
SHIP_CLASSES = _shared['SHIP_CLASSES']
Helper functions (get_body_spin_axis, get_ship_class) remain as Python
code. The module’s public API is unchanged — existing imports work
identically.
JavaScript Consumption
The JSON file is copied into services/web-client/src/generated/sharedConfig.json
at build time (not checked into git).
bodyConfig.jsimportsBODY_PARENTS(moon→parent mappings) from the JSON and adds the planet→Sun and Sun→null entries that the frontend needs for tree building.BODY_SPIN_AXESis imported directly.shipSpecsData.jsimportsSHIP_CLASSESfrom the JSON and merges frontend-only properties (display_name,dimensions) that the backend does not need.
Frontend-only data (colors, spawn altitudes, body subtypes, formatting functions) stays in the JS files.
Build Pipeline
The JSON must be copied to the web-client build context before building:
scripts/build-images.sh: Uses a temp-dir build pattern for web-client (consistent with Python services). Copies the JSON tosrc/generated/sharedConfig.jsoninside the temp dir..github/workflows/build-push.yml: A “Prepare build context (shared config for frontend)” step copies the JSON before building..github/workflows/ci.yml: A copy step before web-client tests ensures the generated file is available forvitest.
Gitignore
services/web-client/.gitignore excludes src/generated/ since the JSON
is copied at build time, not committed.
Shared Validation: Password
Password validation was previously duplicated in the api-gateway and players
services, each hardcoding min_password_length = 8. The shared module now
provides:
MIN_PASSWORD_LENGTH = 8
def validate_password(password: str) -> bool:
"""Check password meets minimum length requirement."""
return isinstance(password, str) and len(password) >= MIN_PASSWORD_LENGTH
- api-gateway (
routes/helpers.py):_validate_passworddelegates togalaxy_config.validate_passwordand raisesHTTPException(400, E012)on failure. - players (
auth.py):validate_passworddelegates togalaxy_config.validate_passwordand returns error code"E012"on failure (preserving the existing return-type contract).
Both services import from galaxy_config, eliminating the duplicated constant.
Invariants
shared_data.jsonis the single source of truth for body parents, spin axes, and ship classes.- Python and JavaScript must produce identical values for all shared keys.
- Adding a new body or ship class requires editing only
shared_data.json. - Frontend-only data (display names, dimensions, colors, spawn altitudes) lives in JS files, not in the JSON.
- Password length requirements are defined once in
galaxy_config(MIN_PASSWORD_LENGTH). Services must not hardcode their own minimum.