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
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 (owned first) |
/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 | GamePage | SVG galaxy map with fleet routes and real-time tween |
/game | GamePage | Admin panel — resource injection, forced tick |
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.