Strategy on a different world every time.
Fustify is a browser strategy game played on deterministic, procedurally generated spherical worlds. Registered players can play locally against heuristic bots or people sharing a device, or create public and private authoritative multiplayer rooms for 2–5 human players.
The application includes:
- a normalized deterministic world generator shared by the globe, minimap, local play, simulation, and hosted matches;
- a default 42-territory, 6-continent world with four local seats;
- random distributed ownership or a complete local round-robin draft;
- reinforcement, deterministic combat, capture movement, fortification, elimination, and victory through one authoritative reducer;
- browser-local versioned saves and shareable versioned setup URLs;
- registered-account multiplayer with durable rooms, seats, immutable published settings, authoritative commands, and polling-based recovery;
- deterministic heuristic controllers, simulations, balance studies, and replayable failure descriptors;
- responsive keyboard- and touch-capable globe, minimap, navigator, HUD, and accessibility behavior;
- deterministic unit, simulation, Playwright, visual-regression, and world-quality verification.
| Route | Purpose |
|---|---|
/ |
Home page, generated-world preview, and account controls |
/local |
Local humans and/or deterministic heuristic bots |
/multiplayer |
Public game browser and private-room creation/joining |
/multiplayer/room/:roomId |
Waiting room, world settings, publication, and seat selection |
/multiplayer/match/:matchId |
Canonical authoritative multiplayer match |
Legacy setup links at /?v=1&... remain supported and open local setup
directly.
The supported zero-setup path is:
git clone https://github.com/christopherjnelson/fustify.git
cd fustify
docker compose up --buildOpen http://localhost:8080. Compose starts PostgreSQL and the combined web/API
service, applies versioned migrations automatically, and persists database data
in the fustify-postgres Docker volume. The development defaults are intended
for localhost only; set POSTGRES_PASSWORD, BETTER_AUTH_SECRET, and
FUSTIFY_ORIGIN before exposing an instance publicly.
Use the Node.js and pnpm versions compatible with package.json:
pnpm install
pnpm devCopy .env.example to .env.local, point DATABASE_URL at a PostgreSQL 17
database, and set a high-entropy BETTER_AUTH_SECRET. pnpm dev starts Vite on
port 5173 and the localhost Node API on port 8787; Vite proxies /api/* to the
API. The API applies pending files in database/migrations/ on startup.
Accounts use email and password through Better Auth. Signup creates a usable session immediately: this project intentionally does not require email verification or any external identity-provider configuration.
Production uses a combined immutable frontend/API release on an Ubuntu droplet behind Caddy. Use the deployment runbook for setup, secrets, deployment, rollback, retention, and recovery.
Core checks:
pnpm test
pnpm test:coverage
pnpm test:simulation
pnpm test:simulation:stress
pnpm test:bot
pnpm test:bot:stress
pnpm lint
pnpm format:check
pnpm build
pnpm bundle:checkBrowser and visual checks:
pnpm exec playwright install chromium
pnpm test:e2e
pnpm test:visual
pnpm test:world-visual
WORLD_AUDIT_PHASE=my-review pnpm audit:world-visualStructured verification and research:
pnpm verify:report
pnpm verify:report:full
pnpm simulate:bots -- --games 10
pnpm study:balance --preset quick
pnpm study:balance --preset thorough --dry-runGenerated-output cleanup:
pnpm clean --dry-run
pnpm clean
pnpm clean:reports --dry-run
pnpm clean:reports
pnpm clean:all --dry-run
pnpm clean:allclean removes reproducible build, coverage, Playwright, bundle-analysis, and
release-staging output. clean:reports explicitly removes ignored verification,
world-generation, balance-study, bot-simulation, and legacy local image
artifacts. clean:all combines them. Cleanup is repository-bound and never
touches .env.local, node_modules, or the PostgreSQL data volume.
Fresh local and multiplayer worlds default to 42 territories and 6 gameplay continents. Local setup starts with four seats—one local human and three heuristic bots—and may be configured for 2–5 seats. Multiplayer supports 2–5 human seats and random assignment. Compatible historical setup data may still contain up to 8 continents or 6 seats.
Version-1 setup URLs serialize:
| Parameter | Default | New-creation range |
|---|---|---|
v |
1 |
1 |
seed |
random | Any non-empty string |
territories |
42 |
12–48 |
continents |
6 |
2–6 |
players |
4 |
Local 2–5; multiplayer 2–5 |
assignment |
random |
random or local draft |
Example:
http://localhost:5173/local?v=1&seed=atlas-prime&territories=42&continents=6&players=4&assignment=random
PlanetDefinition is immutable generated geography. MatchState owns mutable
turn, territory, army, selection, combat, elimination, event, and victory
state. The UI, controllers, simulator, browser persistence, Node initializer,
and multiplayer API all use the same legal-action helpers and
gameReducer; there is no parallel rules implementation.
Generation is deterministic for its seed, setup, generator version, and code version. Random starting positions evaluate a bounded deterministic candidate set. Combat uses a separate deterministic match stream. Cosmetic geographic names do not consume terrain, ownership, controller, or combat randomness.
src/core/contains platform-neutral generation, setup, rules, persistence, controllers, and simulation.src/components/,src/app/, andsrc/browser/contain local application presentation and routing.src/multiplayer/contains room, match synchronization, publication, and browser integration.api/contains the trusted Node HTTP service, authentication integration, and authoritative multiplayer services.database/migrations/contains ordered plain-PostgreSQL schema migrations.scripts/contains development, verification, analysis, study, release, and maintenance tooling.tests/e2e/contains browser interaction and committed visual baselines.
The visual scenario driver remains development-only and requires both
import.meta.env.DEV and visual-review=1. Generated verification output is
ignored under .fustify/reports/, test-results/, coverage/, and
artifacts/.
Use docs/README.md as the documentation index. The primary runbooks are:
- multiplayer
- controllers and simulation
- balance studies
- world generation
- verification
- PostgreSQL and API
- bundle analysis
- deployment
- brand assets
Architecture and operational behavior belong in the relevant active runbook; Git history is the archive for superseded implementation milestones.
Fustify is source-available under the PolyForm Noncommercial License 1.0.0.
You may use, study, modify, and run Fustify for personal and other noncommercial purposes. Commercial use—including paid or advertising-supported hosting, resale, or monetized derivative works—requires prior written permission from Christopher J. Nelson.
The Fustify name and logo may not be used to imply that a derivative project is official, affiliated with, or endorsed by Fustify.
Copyright © 2026 Christopher J. Nelson.