{"slug": "jev-civilizations", "title": "Jev Civilizations", "summary": "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.", "body_md": "[https://thunder-monocle.env-ca.veilstreamapp.com/](https://thunder-monocle.env-ca.veilstreamapp.com/)\n\nJev Civilizations is a small strategy game where four tribes run their own lives and you can only change the world around them.\n\n- **Pick a tribe to support.** Farmers, hunters, mountain fishers, or raiders — each lives off the land differently.\n- **You never give orders.** Instead, every other turn you choose how the environment changes: a wet spring, a\nharsh winter, a wildlife boom, a crop pest. On the turns in between, Nature picks at random.\n- **The tribes decide for themselves.** Each turn an AI model,**Jev** , chooses one action for every tribe from the\nmoves that are actually possible for it — farm, hunt, build, research, move, scout for new land, found a new\nsettlement, or raid a neighbour.\n- **A game engine works out what happens.** Harvests, hunger, births, raids, and growing or shrinking borders are all\ncalculated by ordinary game rules, and the map changes to show it.\n- **See why things happened.** A decision inspector shows exactly what Jev was told and how strongly it preferred each\noption, and an event log explains every result.\n- **The best civilization wins.** After the match (10–200 turns, 50 by default), tribes are ranked by population,\nresilience, technology, and territory.\n\nNo API key? The app also has a clearly labelled **mock mode** that uses a simple built-in policy instead of Jev, so\nyou can play and develop offline.\n\n- Next.js 16 (App Router) + React 19 + TypeScript (strict), exact versions pinned in `package-lock.json`\n- Pure TypeScript game engine in `lib/game/` (no DOM, database, or network)\n- Server-only Jev adapter in `lib/server/jev/` — the only external call the app makes\n- Local SQLite through Node's built-in `node:sqlite` (no native build step, no hosted database)\n- Canvas 2D map, locally drawn SVG art, system fonts; no remote assets, analytics, or telemetry\n\n- **Node.js 24 or newer** (developed on 26.5;`node:sqlite` is built in)\n- npm 11\n- A persistent, writable directory for the SQLite file\n- A TypeSafe API key for live play (optional for the clearly labeled mock mode)\n\nThe default deployment is a **single persistent Node server with a writable volume**. Ephemeral serverless\nfilesystems and multi-instance deployments are not supported (SQLite state and turn leases live on one disk).\n\n```\nnpm install\ncp .env.example .env.local\n# edit .env.local: set TYPESAFE_API_KEY (server-only; never prefix it with NEXT_PUBLIC_)\n```\n\nEnvironment variables (see `.env.example`):\n\n| Variable | Meaning | Default | \n|---|---|---|\n| `TYPESAFE_API_KEY` | Secret used only by the server for `POST https://api.typesafe.ai/v1/systemone` | — | \n| `JEV_MODEL` | Pinned model ID; the resolved model is saved with each turn | `jev-1.13.0` | \n| `DATABASE_PATH` | SQLite file on a persistent writable volume | `./data/jevciv.sqlite` | \n| `ALLOW_MOCK_MODE` | Allow labeled mock simulation games | `false` in production,`true` otherwise | \n| `JEV_TIMEOUT_MS` | Per-attempt timeout | `10000` | \n| `JEV_MAX_ATTEMPTS_PER_TURN` | Total automatic attempts per turn | `3` | \n| `JEV_MAX_ATTEMPTS_PER_GAME` | Cap on the per-game attempt budget (the budget is 3 × match length, up to this cap) | `300` | \n| `JEV_MAX_CONCURRENCY` | Process-wide concurrent Jev calls | `4` | \n| `APP_ORIGIN` | Public origin for same-origin mutation checks | request host | \n| `NEXT_TELEMETRY_DISABLED` | Disable framework telemetry | set to `1` | \n| `RATE_LIMIT_TURNS_PER_MINUTE` ,`RATE_LIMIT_GAMES_PER_HOUR` | Optional overrides of the per-session limits | `60` ,`10` | \n\nNothing to run by hand. On first use the server creates `DATABASE_PATH` (and its directory) and applies the\nschema migrations in `lib/server/migrations.ts`. Migrations are append-only and versioned in `schema_migrations`.\n\n```\nnpm run dev          # development server on http://localhost:3000\nnpm run build        # validates the content catalog, then builds\nnpm start            # production server (PORT/-p to change the port)\n```\n\nSet `APP_ORIGIN` to the exact origin players use (for example `https://civ.example.com`), otherwise\nmutations from the browser are rejected as cross-site.\n\nWith `ALLOW_MOCK_MODE=true`, the start screen offers **Mock simulation**. Decisions then come from a deterministic\nlocal test policy (`lib/game/mockPolicy.ts`), never from Jev. Mode is chosen when a game is created and persisted;\nmock games are labeled \"MOCK SIMULATION\" in the header, inspector, results, replays, and exports. A live game never\nswitches to mock. If live Jev is unavailable, the turn pauses with **Retry** and **Return to menu**.\n\n```\ndocker build -t jev-civilizations .\ndocker run -p 3000:3000 -v jevciv-data:/data \\\n  -e TYPESAFE_API_KEY=... -e APP_ORIGIN=http://localhost:3000 jev-civilizations\n```\n\nThe `Dockerfile` has not been verified in this environment (Docker was not installed during development).\n\n```\nnpm run typecheck\nnpm run lint\nnpm test                      # Vitest: engine, content catalog, Jev adapter (local HTTP stub), orchestration, routes\nnpx playwright install chromium   # once\nnpm run build && npm run test:e2e # Playwright browser tests against `next start` with an in-process Jev fake\nnpm run simulate -- --games 100 --turns 50   # balance smoke run with the MOCK policy (not Jev)\nnpm run validate:catalog\nnpm run check:secrets             # after a build: scans bundles, committable files, and the DB for the key\nnpm run probe:jev                 # optional: ONE real Jev request (uses your key; small cost)\n```\n\nNo test in `npm test` or `npm run test:e2e` contacts the real API. Adapter tests route the fixed endpoint to a local\nHTTP stub; browser tests preload `tests/e2e/stub-fetch.mjs`, which answers the fixed endpoint in-process.\n\n- **Export** : the header's*Export* button (or`GET /api/games/:id/export` ) downloads JSON with every turn's exact\nJev request (without the Authorization header), validated output, attempts, usage, outcomes, and state hashes.\nExports never include keys, cookies, session IDs, or database paths.\n- **Replay** :*Replay match* (or`/game/:id/replay` ) steps through recorded map deltas, events, and decisions.\nReplays make no model calls and are labeled REPLAY.`verifyReplay()` in`lib/server/replay.ts` re-runs the engine\nfrom the recorded initial state and recorded choices and checks every post-turn state hash (covered by tests).\n- A new match on the same seed reproduces the same initial world but is a new live run, not a replay.\n\nAll state is in the SQLite file at `DATABASE_PATH` (WAL mode, so also `-wal`/`-shm` files while running).\n\n``` js\n# Consistent online backup\nnode -e \"const {DatabaseSync}=require('node:sqlite');new DatabaseSync(process.argv[1]).exec(\\\"VACUUM INTO '\\\"+process.argv[2]+\\\"'\\\")\" data/jevciv.sqlite backup.sqlite\n# Restore: stop the server, replace the file, remove stale -wal/-shm files, start the server\n```\n\nPlayers 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).\n\n```\napp/                 pages (/, /game/[id], /game/[id]/replay) and same-origin API route handlers\ncomponents/          start screen, Canvas map, event card, scoreboard, inspector, results, replay\ncontent/             authored catalog: tribes, events (32×3), actions, technologies, balance, narration, help\nlib/game/            pure engine: world generation, candidates, resolution, effects, economy, scoring, views\nlib/server/          config, SQLite, sessions, limits, Jev adapter, turn orchestration, replay/export\nlib/client/          browser API client, world decoding, game controller hook\nscripts/             catalog validation, balance simulation, live probe, secret scan\ntests/               engine, content, server (stub Jev), e2e (Playwright)\npublic/art/          local SVG emblems and illustrations\n```\n\nReleased under the **MIT License** (see `LICENSE`), © 2026 Steven Jones. Third-party packages are used under their\nown licenses (see `THIRD_PARTY_NOTICES.md`).\n\nThis project is **not affiliated with or endorsed by TypeSafe**. Live play uses your own TypeSafe API key, and you are\nresponsible for its terms and costs. Game content is fiction. See `DISCLAIMER.md` for details, `SECURITY.md` to report\nvulnerabilities and for key handling, and `CONTRIBUTING.md` to contribute.", "url": "https://wpnews.pro/news/jev-civilizations", "canonical_source": "https://github.com/JonesSteven/jev_civilization", "published_at": "2026-09-29 10:27:46+00:00", "updated_at": "2026-09-29 10:48:10.937161+00:00", "lang": "en", "topics": ["ai-agents", "ai-products", "artificial-intelligence"], "entities": ["Jev Civilizations", "Jev", "Next.js 16", "React 19", "TypeScript", "Node.js 24", "node:sqlite", "TypeSafe"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/jev-civilizations", "markdown": "https://wpnews.pro/news/jev-civilizations.md", "text": "https://wpnews.pro/news/jev-civilizations.txt", "jsonld": "https://wpnews.pro/news/jev-civilizations.jsonld"}}