Jev Civilizations Jev Civilizations is a strategy game in which an AI model named Jev selects one action per turn for each of four tribes — farmers, hunters, mountain fishers, and raiders — while the player only alters the environment every other turn. The game runs on Next.js 16, React 19, and TypeScript, uses a server-only Jev adapter calling POST https://api.typesafe.ai/v1/systemone with a pinned default model ID of jev-1.13.0, and stores state in local SQLite via Node's built-in node:sqlite. Matches run 10–200 turns (50 by default), and a labeled mock mode allows offline play without a TypeSafe API key. https://thunder-monocle.env-ca.veilstreamapp.com/ https://thunder-monocle.env-ca.veilstreamapp.com/ Jev Civilizations is a small strategy game where four tribes run their own lives and you can only change the world around them. - Pick a tribe to support. Farmers, hunters, mountain fishers, or raiders — each lives off the land differently. - You never give orders. Instead, every other turn you choose how the environment changes: a wet spring, a harsh winter, a wildlife boom, a crop pest. On the turns in between, Nature picks at random. - The tribes decide for themselves. Each turn an AI model, Jev , chooses one action for every tribe from the moves that are actually possible for it — farm, hunt, build, research, move, scout for new land, found a new settlement, or raid a neighbour. - A game engine works out what happens. Harvests, hunger, births, raids, and growing or shrinking borders are all calculated by ordinary game rules, and the map changes to show it. - See why things happened. A decision inspector shows exactly what Jev was told and how strongly it preferred each option, and an event log explains every result. - The best civilization wins. After the match 10–200 turns, 50 by default , tribes are ranked by population, resilience, technology, and territory. No API key? The app also has a clearly labelled mock mode that uses a simple built-in policy instead of Jev, so you can play and develop offline. - Next.js 16 App Router + React 19 + TypeScript strict , exact versions pinned in package-lock.json - Pure TypeScript game engine in lib/game/ no DOM, database, or network - Server-only Jev adapter in lib/server/jev/ — the only external call the app makes - Local SQLite through Node's built-in node:sqlite no native build step, no hosted database - Canvas 2D map, locally drawn SVG art, system fonts; no remote assets, analytics, or telemetry - Node.js 24 or newer developed on 26.5; node:sqlite is built in - npm 11 - A persistent, writable directory for the SQLite file - A TypeSafe API key for live play optional for the clearly labeled mock mode The default deployment is a single persistent Node server with a writable volume . Ephemeral serverless filesystems and multi-instance deployments are not supported SQLite state and turn leases live on one disk . npm install cp .env.example .env.local edit .env.local: set TYPESAFE API KEY server-only; never prefix it with NEXT PUBLIC Environment variables see .env.example : | Variable | Meaning | Default | |---|---|---| | TYPESAFE API KEY | Secret used only by the server for POST https://api.typesafe.ai/v1/systemone | — | | JEV MODEL | Pinned model ID; the resolved model is saved with each turn | jev-1.13.0 | | DATABASE PATH | SQLite file on a persistent writable volume | ./data/jevciv.sqlite | | ALLOW MOCK MODE | Allow labeled mock simulation games | false in production, true otherwise | | JEV TIMEOUT MS | Per-attempt timeout | 10000 | | JEV MAX ATTEMPTS PER TURN | Total automatic attempts per turn | 3 | | JEV MAX ATTEMPTS PER GAME | Cap on the per-game attempt budget the budget is 3 × match length, up to this cap | 300 | | JEV MAX CONCURRENCY | Process-wide concurrent Jev calls | 4 | | APP ORIGIN | Public origin for same-origin mutation checks | request host | | NEXT TELEMETRY DISABLED | Disable framework telemetry | set to 1 | | RATE LIMIT TURNS PER MINUTE , RATE LIMIT GAMES PER HOUR | Optional overrides of the per-session limits | 60 , 10 | Nothing to run by hand. On first use the server creates DATABASE PATH and its directory and applies the schema migrations in lib/server/migrations.ts . Migrations are append-only and versioned in schema migrations . npm run dev development server on http://localhost:3000 npm run build validates the content catalog, then builds npm start production server PORT/-p to change the port Set APP ORIGIN to the exact origin players use for example https://civ.example.com , otherwise mutations from the browser are rejected as cross-site. With ALLOW MOCK MODE=true , the start screen offers Mock simulation . Decisions then come from a deterministic local test policy lib/game/mockPolicy.ts , never from Jev. Mode is chosen when a game is created and persisted; mock games are labeled "MOCK SIMULATION" in the header, inspector, results, replays, and exports. A live game never switches to mock. If live Jev is unavailable, the turn pauses with Retry and Return to menu . docker build -t jev-civilizations . docker run -p 3000:3000 -v jevciv-data:/data \ -e TYPESAFE API KEY=... -e APP ORIGIN=http://localhost:3000 jev-civilizations The Dockerfile has not been verified in this environment Docker was not installed during development . npm run typecheck npm run lint npm test Vitest: engine, content catalog, Jev adapter local HTTP stub , orchestration, routes npx playwright install chromium once npm run build && npm run test:e2e Playwright browser tests against next start with an in-process Jev fake npm run simulate -- --games 100 --turns 50 balance smoke run with the MOCK policy not Jev npm run validate:catalog npm run check:secrets after a build: scans bundles, committable files, and the DB for the key npm run probe:jev optional: ONE real Jev request uses your key; small cost No test in npm test or npm run test:e2e contacts the real API. Adapter tests route the fixed endpoint to a local HTTP stub; browser tests preload tests/e2e/stub-fetch.mjs , which answers the fixed endpoint in-process. - Export : the header's Export button or GET /api/games/:id/export downloads JSON with every turn's exact Jev request without the Authorization header , validated output, attempts, usage, outcomes, and state hashes. Exports never include keys, cookies, session IDs, or database paths. - Replay : Replay match or /game/:id/replay steps through recorded map deltas, events, and decisions. Replays make no model calls and are labeled REPLAY. verifyReplay in lib/server/replay.ts re-runs the engine from the recorded initial state and recorded choices and checks every post-turn state hash covered by tests . - A new match on the same seed reproduces the same initial world but is a new live run, not a replay. All state is in the SQLite file at DATABASE PATH WAL mode, so also -wal / -shm files while running . js Consistent online backup node -e "const {DatabaseSync}=require 'node:sqlite' ;new DatabaseSync process.argv 1 .exec \"VACUUM INTO '\"+process.argv 2 +\"'\" " data/jevciv.sqlite backup.sqlite Restore: stop the server, replace the file, remove stale -wal/-shm files, start the server Players keep access through their anonymous session cookie; restoring the database restores their matches. A complete 50-turn game uses about 4 MB of storage turn records, map changes, and a full snapshot every ten turns . app/ pages /, /game/ id , /game/ id /replay and same-origin API route handlers components/ start screen, Canvas map, event card, scoreboard, inspector, results, replay content/ authored catalog: tribes, events 32×3 , actions, technologies, balance, narration, help lib/game/ pure engine: world generation, candidates, resolution, effects, economy, scoring, views lib/server/ config, SQLite, sessions, limits, Jev adapter, turn orchestration, replay/export lib/client/ browser API client, world decoding, game controller hook scripts/ catalog validation, balance simulation, live probe, secret scan tests/ engine, content, server stub Jev , e2e Playwright public/art/ local SVG emblems and illustrations Released under the MIT License see LICENSE , © 2026 Steven Jones. Third-party packages are used under their own licenses see THIRD PARTY NOTICES.md . This project is not affiliated with or endorsed by TypeSafe . Live play uses your own TypeSafe API key, and you are responsible for its terms and costs. Game content is fiction. See DISCLAIMER.md for details, SECURITY.md to report vulnerabilities and for key handling, and CONTRIBUTING.md to contribute.