Architecture
Monorepo structure, backend layers, frontend patterns, and infrastructure.
Repository Layout
| Directory | Purpose |
|---|---|
backend/Star.Api | ASP.NET 10 REST API |
frontend/ | React 19 + Vite game client |
ships/ | Three.js ship blueprint visualizer (standalone) |
planets/ | Node.js + Puppeteer planet video pipeline |
docs/ | This documentation site (Nuxt + shadcn-docs-nuxt) |
protos/ | Protobuf definitions (star/v1/*.proto) |
infra/docker/ | Docker Compose for local Postgres and docs |
All tasks are run via mise: mise run <task>.
Backend
Stack: ASP.NET 10 Minimal APIs · EF Core 10 + Npgsql · FluentResults · FluentValidation · Clerk JWT auth
Layer Structure
Server Model
The game supports multiple servers. Each server gets its own generated galaxy. Only one server is Active at a time — this is the server all players interact with.
| Endpoint | Purpose |
|---|---|
GET /api/v1/servers/active | Returns the currently active server |
GET /api/v1/admin/servers | List all servers |
POST /api/v1/admin/servers | Create a server and generate its galaxy |
POST /api/v1/admin/servers/{id}/activate | Set a server as active (deactivates all others) |
POST /api/v1/admin/servers/{id}/reset | Wipe and regenerate a server's galaxy |
DELETE /api/v1/admin/servers/{id} | Permanently delete a server |
Galaxy generation is seeded and deterministic: the same seed + config produces the same galaxy every time. Sectors are laid out in a 3-column grid with 600-unit spacing, guaranteeing no overlap between sector coordinate zones even accounting for planet orbits. Each sector contains 1–2 suns (30% chance of a binary pair); system proximity to the nearest sun shifts the habitable zone and determines how frozen or temperate a system's planets are.
API Response Envelope
All endpoints return this envelope. FluentResults is used internally and converted at the endpoint boundary.
Auth
Clerk JWT by default. Set DISABLE_AUTH=true to use DemoAuthHandler (injects an anonymous user) for local dev.
Configuration
Loaded from .env (or env.demo for APP_ENV=demo) with .env.local overlay.
| Variable | Purpose |
|---|---|
DB_CONNECTION | Postgres connection string |
CORS_ORIGINS | Comma-separated allowed origins |
CLERK_ISSUER | Clerk JWT issuer URL |
DISABLE_AUTH | Skip auth (local dev) |
API_VERSION | Shown in Scalar API docs |
TICK_INTERVAL_SECONDS | Economy tick interval in seconds (default: 300) |
Frontend
Stack: React 19 · Vite 7 · TailwindCSS v4 · Zustand · TanStack Query · Clerk · React Router v7
Key Patterns
src/api/— typedapiFetchwrappers per domain, each acceptinggetTokencallbacksrc/stores/gameStore.ts— Zustand for cross-page state (selectedPlanetId,lastTickResults)src/pages/— route-level components, data fetching via TanStack QueryVITE_DISABLE_AUTH=truebypasses Clerk (must match backendDISABLE_AUTH=true)
Pages
| Route | Component | Purpose |
|---|---|---|
/planets | PlanetsPage | List all planets grouped by sector → system (active server) |
/planets/:id | PlanetDetailPage | Stockpiles, buildings, production queue, trade routes, economy panel |
/hangar | HangarPage | Blueprint list and designer |
/fleets | FleetsPage | Fleet list, ship stacks, dispatch modal |
/map | GalaxyMapPage | SVG galaxy map with fleet routes and real-time tween |
/game | GamePage | Resource injection, forced tick (dev) |
/admin | AdminPage | Server management — create, activate, reset, delete |
Ships Visualizer
A self-contained TypeScript + Three.js app at ships/ with no framework. Runs on port 5174.
Key files:
shipBuilder.ts— assembles 3D hull from frame profile + module placementsrenderer.ts— Three.js scene with blueprint grid and OrbitControlsui.ts— DOM-based slot assignment UIframes.ts/modules.ts— static definitions matching backend seed data
Infrastructure
Local Dev
Docker Compose Services
| Service | Port | Purpose |
|---|---|---|
db | 5434 | Postgres 17 |
docs | 4000 | This documentation site |
Migrations
Determinism Requirements
All game simulation must be:
- Pure function based — same inputs → same outputs, always
- Replayable — any game state can be replayed from event log
- Hash verifiable — combat results signed with
sha256(inputs + engine_version)
This applies to the economy calculators and especially to the combat engine. No Random, no DateTime.Now inside simulation logic.