An original-IP, browser-based, turn-based fantasy strategy game — a homage to the Heroes-of-Might-and-Magic-III design space that copies nothing. TypeScript, pnpm monorepo, deterministic headless core + PixiJS client.
pnpm install # requires Node >= 22
For real art generation, also copy .env.example → .env and add your PIXELLAB_API_KEY (see below).
| Command | What it does |
|---|---|
pnpm art:studio |
Open the art studio — generate, preview, compare, and pick real sprites in the browser (http://localhost:5188) |
pnpm art:generate |
Headless art CLI — prints the planned calls + cost (dry-run by default; spends nothing without --confirm-spend) |
pnpm placeholders |
Regenerate the free labeled placeholder sprites for every asset |
pnpm dev:client |
Run the game client (Vite dev server) |
pnpm dev:server |
Run the game server |
pnpm test |
Run the unit tests (vitest) |
pnpm typecheck / pnpm check |
Typecheck all packages / run Biome lint + format check |
pnpm art:studio # the whole inventory
pnpm art:studio --category creature --clips still # just creatures, cheap stills
It reads PIXELLAB_API_KEY from a gitignored repo-root .env (see .env.example). Without a key it still opens in read-only mode (browse + pick + assemble already-staged candidates; generation disabled).
Full operator's guide — flags, cost cheat-sheet, where files land: packages/tools/README.md.
Live at https://openheroes.topherhunt.com, on the VPS aliased racknerd1, behind Caddy (auto-TLS). One Node process serves the client, audio, REST API, /ws, and /health on 127.0.0.1:3003; it runs as the unprivileged openheroes systemd service. Rooms are in-memory, so any restart drops live games. All scripts read scripts/deploy.env (gitignored) via scripts/config.sh.
chmod +x scripts/*.sh(once after clone; or run any script asbash scripts/<name>.sh).- DNS: A record
openheroes.topherhunt.com→ the VPS IP. Verify:dig openheroes.topherhunt.com +short. cp scripts/deploy.env.example scripts/deploy.env, setDEPLOY_HOST=racknerd1andDOMAIN=openheroes.topherhunt.com. ConfirmAPP_PORT=3003is free first:ssh racknerd1 cat /srv/registry/*.app../scripts/host-setup.sh— installs shared host infra (Node, pnpm, Caddy, firewall). Idempotent; safe on the shared box../scripts/provision.sh— creates theopenheroesuser,/srv/openheroes, the systemd unit, and the Caddy route. Refuses to reuse a port already claimed by another app.
./scripts/deploy.sh # build client locally, rsync (incl. dist), pnpm install on host, restart, health-check
The memory-heavy Vite build runs on your machine and ships the prebuilt packages/client/dist; the ~1 GB VPS only runs pnpm install (for the server's runtime deps + native better-sqlite3), so a deploy never risks OOMing the box.
./scripts/status.sh # systemd state, listening socket, health pings, crash summary, resource usage
./scripts/restart.sh # restart (drops in-memory games); start.sh / stop.sh also available
./scripts/logs.sh # tail the standard service logs (journald); Ctrl-C to stop
./scripts/download-logs.sh # export journald logs to ./logs/
./scripts/crash-log.sh # print the dedicated crash log; `-f` to follow, `download` to save locally
On any uncaught crash the server records a full report (timestamp, stack, uptime, memory) to both the standard log (journald, via logs.sh) and a dedicated crash-only file at /srv/openheroes/logs/crash.log (via crash-log.sh), then systemd restarts it.
| Package | Role |
|---|---|
packages/shared |
Types, enums, sprite/audio manifest schemas |
packages/core |
Deterministic game engine — no DOM, RNG, wall-clock, or network |
packages/content |
Creatures / heroes / spells / towns / artifacts (zod-validated) |
packages/server |
Server-authoritative, command-based game server |
packages/client |
PixiJS renderer + UI |
packages/tools |
Build-time asset pipeline (art + music) — never shipped in the game |
docs/00-status.md— read this first: current status + per-task reading routesdocs/00-canon.md— the locked design source of truthdocs/— architecture, gameplay, asset pipelines, worldbuilding, and ADRs