A browser-based, API-first sci-fi idle RPG set in the corporate-controlled solar system Hibaro-5.
Heliora is a fully playable sci-fi idle RPG where you control a character navigating the dark, corporate-controlled solar system of Hibaro-5. Take gigs, jobs, and quests; travel between planets; rest in safehouses; trade gear at shops and contraband on the black market; speculate on corporate stock; and watch the world tick around you while factions, corporations, and rival operators reshape the economy.
The game runs as a browser game at http://localhost:3001 once the API and web app are up. The world advances on a server-side auto-tick every 30 seconds, so opportunities you accept resolve themselves while you're away — true idle progression. The dashboard and opportunity board now subscribe to a live SSE stream, so tick completions and NPC-world changes show up without waiting for browser polling.
- Start everything (see Local Setup below) and open http://localhost:3001.
- Log in as
test_player/Heliora123, or register a new operator. - The dashboard shows your character — credits, health, energy, wanted level, location, and any opportunities currently in progress.
- Click OPPORTUNITIES in the nav to accept gigs, jobs, and quest-chain steps. Story quests can unlock follow-up quests, and one-off quest steps disappear once completed.
- TRAVEL lets you move between planets, districts, and buildings. Higher danger / lower law = higher cost, more wanted-level risk, and a bigger energy hit. District-controlling factions now also apply reputation-based warnings, hostile surcharges, or hard lockouts.
- SHOP trades gear with whichever building you're currently inside. Black markets pay a contraband bonus on sales but bringing contraband into a high-law district may raise your wanted level.
- MARKET is the corporate stock exchange — buy and sell shares; prices swing every world tick based on revenue, debt, world events, and bankruptcy risk.
- INVENTORY shows what you're carrying and lets you use consumables (e.g. medical patches restore health).
- LOGS is the audit trail of everything you've done.
- While inside a safehouse, clinic, or hub you can REST from the dashboard to recover health/energy and reduce heat (safehouses only).
| Layer | Technology |
|---|---|
| Backend API | NestJS (TypeScript) |
| Player Web App | Next.js 15 + Tailwind |
| Admin Control Plane | Next.js 15 |
| Database | PostgreSQL 16 |
| ORM | Prisma 5 |
| Queue | BullMQ + Redis 7 |
| Validation | Zod + NestJS ValidationPipe |
| API Docs | Swagger/OpenAPI |
| Testing | Jest |
| Linting | ESLint + Prettier |
| Local Infrastructure | Docker Compose |
hibaro-5/
├── apps/
│ ├── api/ # NestJS REST API
│ │ └── src/
│ │ ├── modules/ # Feature modules (players, characters, etc.)
│ │ └── prisma/ # Prisma service
│ ├── admin/ # Next.js admin control plane
│ └── worker/ # BullMQ background worker
├── packages/
│ ├── game-rules/ # Pure game logic (no NestJS dependencies)
│ ├── game-types/ # Shared TypeScript types
│ ├── platform-sdk/ # Shared API client + realtime contracts
│ └── validation/ # Zod schemas
├── prisma/
│ ├── schema.prisma # Database schema
│ └── seed.ts # Seed data
├── docker-compose.yml # Postgres + Redis
└── README.md
- Node.js 20+
- Docker + Docker Compose
- npm 9+
npm run setup:run
npm run setup:stopThat script will:
- install dependencies when needed
- create
.envfrom.env.exampleif missing - start Postgres and Redis with Docker Compose
- wait for both services to accept connections
- run Prisma migrations and seed data
- launch the API, player web app, admin app, and worker together
Optional flags:
./scripts/setup-and-run.sh --help
./scripts/setup-and-run.sh --no-worker
./scripts/setup-and-run.sh --force-install
./scripts/setup-and-run.sh --sudo-docker
./scripts/stop-and-clean.sh --help
./scripts/stop-and-clean.sh --docker
./scripts/stop-and-clean.sh --sudo-dockerIf a previous run left ports 3000, 3001, or 3002 occupied, stop the stale processes first:
npm run setup:stop
npm run setup:stop -- --docker --sudo-docker# 1. Clone and install dependencies
git clone https://github.com/monjar/Hibaro-5.git
cd Hibaro-5
npm install
# 2. Set up environment
cp .env.example .env
# Edit .env if needed (defaults work with docker-compose)
# 3. Start Postgres and Redis
docker compose up -d
# 4. Run database migrations (creates tables + applies any new migrations)
npm run prisma:migrate
# 5. Seed the world (idempotent – safe to re-run after pulling new migrations)
npm run db:seed
# 6. Start the API
npm run dev
# 7. Start the player web app
npm run dev:web
# 8. Start the admin control plane
npm run dev:admin
# API is now running at http://localhost:3000
# Player web app at http://localhost:3001
# Admin control plane at http://localhost:3002
# Swagger docs at http://localhost:3000/api/docs| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
postgresql://heliora:heliora@localhost:5432/heliora |
PostgreSQL connection string |
REDIS_URL |
redis://localhost:6379 |
Redis connection string |
PORT |
3000 |
API server port |
NODE_ENV |
development |
Environment mode |
JWT_SECRET |
dev-secret |
Required — JWT signing key |
JWT_EXPIRES_IN |
1d |
JWT validity duration |
ADMIN_TOKEN |
(unset) | Shared secret for admin CRUD endpoints. When unset, admin writes are open (local dev). Set to any string to gate POST/PATCH/DELETE on opportunities, locations, factions, corporations, world events, and item definitions. |
SIMULATION_AUTO_TICK |
true |
Set to false to disable the in-process world-tick scheduler |
SIMULATION_TICK_INTERVAL_MS |
30000 |
Auto-tick interval |
NEXT_PUBLIC_API_URL |
http://localhost:3000 |
API base URL the web/admin apps point at |
The Prisma schema defines all entities. Key commands:
# Generate Prisma client (run after schema changes)
npm run prisma:generate
# Run migrations
npm run prisma:migrate
# Seed the database
npm run db:seed
# Open Prisma Studio (GUI)
npx prisma studio --schema=prisma/schema.prismaRunning npm run db:seed creates:
- Hibaro-5 — corporate-controlled solar system
- Antrolus — industrial starting world
- Teraluma — prosperous civic world
- Los Panko — neon-lit port world
- Pigeon95 — logistics hub
- Valerina — remote blacksite world
- Red Market — underground trading network
- Coil Union — labor union
- Glasswater Civic Authority — civic government
- Valerina Ghosts — shadowy collective
- Pigeon Corporation (LOGISTICS) — controls Pigeon95
- Helix Dynamics (ENERGY) — Antrolus & Teraluma presence
- Blue Orchard Biotech (MEDICAL) — Valerina secrets
- SunSpoke Media (MEDIA) — controls information
- Username:
test_player - Default password:
Heliora123(override withSEED_TEST_PLAYER_PASSWORD) - Character: Nova Rook
- Starting location: Antrolus / Arrival Yard / Arrival Processing Hub
- Starting credits: 250
- Welcome to Antrolus — onboarding quest that unlocks the next investigation step
- Something in the Cargo — Coil Union follow-up that opens the Pigeon95 trail
- Pigeon95 Secret — final investigation step, with corporation-standing lockout support
GET /health
POST /auth/register # Register a player and starter character
POST /auth/login # Log in with username/email + password
GET /auth/me # Authenticated player profile
GET /players/:id # Get your player with character (JWT required)
GET /players/:id/activity # Get your activity log (JWT required)
GET /characters/:id # Get your character details (JWT required)
GET /characters/:id/summary # Your character + memberships + activity (JWT required)
GET /characters/:id/location # Your current location (JWT required)
GET /characters/:id/inventory # All items owned by your character (JWT required)
GET /characters/:id/relationships # Your faction/corp reputation etc. (JWT required)
POST /characters/:id/travel # Move your character to a new location (JWT required)
Body: { "planetId": "...", "districtId": "...", "buildingId": "..." }
POST /characters/:id/travel/quote # Preview travel cost / risk + faction-standing penalties (JWT required)
POST /characters/:id/rest # Recover at a safehouse / clinic / hub (JWT required)
POST /characters/:id/items/:itemId/use # Consume a consumable item (JWT required)
POST /simulation/tick # Run one simulation tick manually
GET /simulation/world-state # Snapshot of planets, districts, corps, events, etc.
GET /simulation/history?limit=10 # Recent tick history
GET /simulation/realtime-contracts # Shared realtime event contract metadata
GET /simulation/stream # Server-sent event stream for tick + NPC updates
GET /shops/:buildingId # List items for sale at a shop building
POST /shops/:buildingId/buy # Buy an item (must be inside building) (JWT required)
Body: { "itemInstanceId": "...", "characterId": "..." }
POST /shops/:buildingId/sell # Sell an owned item to a shop (JWT required)
Body: { "itemInstanceId": "...", "characterId": "..." }
GET /stocks/market # Public stock quotes for all listed corporations
GET /stocks/holdings/:characterId # Your portfolio (JWT required)
POST /stocks/buy # Buy shares (JWT required)
Body: { "characterId": "...", "corporationId": "...", "shares": 5 }
POST /stocks/sell # Sell shares (JWT required)
Body: { "characterId": "...", "corporationId": "...", "shares": 5 }
GET /locations/solar-systems # All solar systems
GET /locations/planets # All planets
GET /locations/planets/:id # Planet with districts
GET /locations/districts # All districts (admin)
GET /locations/districts/:id # District with buildings
GET /locations/buildings # All buildings (admin)
GET /locations/buildings/:id # Building details
POST /locations/planets # Create a planet (admin)
PATCH /locations/planets/:id # Update a planet (admin)
DELETE /locations/planets/:id # Delete a planet (admin)
POST /locations/districts # Create a district (admin)
PATCH /locations/districts/:id # Update a district (admin)
DELETE /locations/districts/:id # Delete a district (admin)
POST /locations/buildings # Create a building (admin)
PATCH /locations/buildings/:id # Update a building (admin)
DELETE /locations/buildings/:id # Delete a building (admin)
GET /factions # All factions
GET /factions/:id # Faction details
POST /factions # Create a faction (admin)
PATCH /factions/:id # Update a faction (admin)
DELETE /factions/:id # Delete a faction (admin)
GET /corporations # All corporations
GET /corporations/:id # Corporation details
POST /corporations # Create a corporation (admin)
PATCH /corporations/:id # Update a corporation (admin)
DELETE /corporations/:id # Delete a corporation (admin)
GET /items/definitions # All item definitions
GET /items/definitions/:id # Item definition by ID
POST /items/definitions # Create an item definition (admin)
PATCH /items/definitions/:id # Update an item definition (admin)
DELETE /items/definitions/:id # Delete an item definition (admin)
GET /opportunities # All opportunity definitions
GET /opportunities/available/:characterId # Available for your character (JWT required)
GET /opportunities/instances/:characterId # Your accepted opportunities (JWT required)
POST /opportunities/:opportunityId/accept # Accept opportunity for your character (JWT required)
Body: { "characterId": "..." }
POST /opportunities/instances/:instanceId/resolve # Manually resolve your instance (JWT required)
POST /opportunities # Create an opportunity definition (admin)
PATCH /opportunities/:id # Update an opportunity definition (admin)
DELETE /opportunities/:id # Delete an opportunity definition (admin)
POST /simulation/tick # Resolve all due opportunities + world events
GET /simulation/world-state # Snapshot of planets/factions/corps/events
GET /simulation/history # Recent step-based simulation tick history
GET /simulation/realtime-contracts # Shared realtime event contracts
GET /world-events # All world events
GET /world-events/active # Currently active events
GET /world-events/:id # World event details
POST /world-events # Create a world event (admin)
PATCH /world-events/:id # Update a world event (admin)
DELETE /world-events/:id # Delete a world event (admin)
The fastest way to play is the web UI at http://localhost:3001:
- Log in (
test_player/Heliora123). - From OPPORTUNITIES, accept any gig you qualify for.
- Wait — the auto-tick scheduler resolves due jobs every ~30s. The dashboard's "In Progress" panel shows the timer.
- Hit TRAVEL → Antrolus → Furnace Row → Furnace Row Underground to enter the black market.
- Open SHOP to buy a Smuggler Toolkit (boosts smuggling success); sell unwanted gear back here for credits.
- Open MARKET and buy a few PGN (Pigeon Corporation) shares. Prices update on every world tick.
- Travel back to the Arrival Yard and REST at the Red Market Safehouse to refill energy and shave off wanted level.
- Repeat.
For headless testing — every authenticated route uses Authorization: Bearer <token>.
TOKEN=$(curl -s -X POST http://localhost:3000/auth/login \
-H "Content-Type: application/json" \
-d '{"identifier":"test_player","password":"Heliora123"}' | jq -r .accessToken)
CHAR_ID=$(curl -s -H "Authorization: Bearer $TOKEN" http://localhost:3000/auth/me | jq -r .character.id)curl -H "Authorization: Bearer $TOKEN" http://localhost:3000/opportunities/available/$CHAR_ID
curl -X POST http://localhost:3000/opportunities/opp-move-medical-crates/accept \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"characterId\":\"$CHAR_ID\"}"curl http://localhost:3000/stocks/market
curl -X POST http://localhost:3000/stocks/buy \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"characterId\":\"$CHAR_ID\",\"corporationId\":\"<corpId>\",\"shares\":3}"curl -X POST http://localhost:3000/simulation/tickcurl http://localhost:3000/simulation/world-stateThe Next.js admin app at http://localhost:3002 manages every CMS-style entity in the world:
- Overview (
/) — tick history, market state, district control, NPC activity, realtime contracts. - Opportunities (
/opportunities) — gigs, jobs, quests with JSON editors for requirements, rewards, risks, and JOB cadence. - Locations (
/locations) — planets, districts, and buildings under one tabbed page. - Factions (
/factions) — name, ideology, treasury, influence, optional HQ building. - Corporations (
/corporations) — industry, status, cash/debt/revenue, stock ticker/price/volatility, bankruptcy risk. - World Events (
/world-events) — schedule events with scope, effects JSON, and timed activation. - Items (
/items) — item definitions with category-specific JSON for weapon/clothing/tool/vehicle data.
Every write goes through AdminGuard. To enable the gate, set ADMIN_TOKEN=<your secret> in .env, restart the API, and paste the same token into the Admin token widget at the top of any admin page (it's stored in localStorage and sent as x-admin-token on every write). With ADMIN_TOKEN left blank the admin panel is open — fine for local dev, never deploy that way.
Deletes refuse to cascade silently — for example you can't delete a planet that still has districts or characters on it, or a corporation with active stock holdings. Resolve those references first.
# Run all tests (game-rules + API)
npm test
# Run game-rules tests only
npm run test --workspace=packages/game-rules
# Run API tests only
npm run test --workspace=apps/api
# With coverage
npm run test:cov --workspace=packages/game-rulesThe codebase is a modular monolith structured for easy extraction into microservices later. Each NestJS module is self-contained with its own controller, service, and module definition.
All game state lives in PostgreSQL. Redis is used only for queues and future caching.
The packages/game-rules package contains all game logic as pure TypeScript functions with no NestJS or database dependencies. This makes them:
- Fully testable in isolation
- Portable to other contexts (admin tools, simulations)
- Deterministic when given a
randomSeed
Key functions:
checkRequirement(character, requirement, context)— validates a single requirementcheckRequirements(character, requirements, context)— validates all requirementscalculateOpportunitySuccessChance(character, opportunity)— stat-based success probabilityrollOpportunityOutcome(character, opportunity, randomSeed?)— roll success/fail + rewards/risksresolveOpportunity(character, opportunity, randomSeed?)— full resolution with character updates
The API has an in-process auto-tick scheduler (see apps/api/src/modules/simulation/simulation.scheduler.ts) that runs the full world tick every 30 seconds — resolving due opportunities, advancing the economy, repricing stocks, expiring/activating world events, and recording NPC actions. Tick interval is configurable via SIMULATION_TICK_INTERVAL_MS; auto-tick can be disabled with SIMULATION_AUTO_TICK=false.
The /simulation/tick endpoint still manually triggers a tick on demand (useful for tests).
A BullMQ + Redis path exists in apps/worker and can be used for sharded background processing — currently the in-process scheduler is sufficient for single-instance deployments.
Controllers only handle HTTP concerns (routing, request parsing, response serialization). All business logic lives in services. Game rules stay in the packages/game-rules package.
# Start the worker separately
cd apps/worker
npm install
npm run dev
# Or with Docker (TODO: add worker to docker-compose)- docs/PROJECT-STATE.md — the factual inventory: architecture, every shipped system, content counts, verification state, and known gaps.
- docs/GAME-ASSESSMENT.md — the qualitative assessment: how the game plays hour-by-hour, loop and balance analysis, a scorecard, and the launch-readiness verdict.
- PLAN.md — the development plan: future ideas plus detailed shipping notes for every completed feature.
The README below covers setup and the API surface; the feature set has grown well beyond the highlights listed here — PROJECT-STATE.md is the authoritative systems list (progression/levels, equipment, mid-mission decisions, PVP, housing, crafting, faction wars, world-event effects, daily rewards, achievements, and more).
See PLAN.md for the full development plan — what's up next, what's in progress, and future ideas — with file-level context for each item.
- Player web app (dashboard, opportunities, inventory, travel, shop, stock market, activity log)
- JWT auth — register/login/me, browser session with localStorage
- Auto-tick scheduler — 30s in-process world tick, configurable via env
- Travel costs and quote preview
- Shops and black markets — contraband premium, heat mechanic
- Stock market — per-corp prices move each tick; player portfolios with avg-cost basis and P/L
- Safehouse / clinic / hub rest — health, energy, wanted-level recovery
- Consumable items — use from inventory
- NPC simulation, economy drift, corporation boom/bust, faction district control
- Admin control plane with tick observability and full CRUD for opportunities, planets/districts/buildings, factions, corporations, world events, and item definitions — all gated behind an
ADMIN_TOKEN