@katforge/codex

The codex is KATFORGE's game-data layer. For every supported game it stores definitions (items, monsters, spells, quests, ...) and the art (icons, portraits, audio) that goes with them, with full per-patch history.

It exists so server-side transactions can validate against authoritative game data, the SDKs can serve clients a consistent view, and tooling can answer "what changed in this patch" with one HTTP call.

Quick look

One fetch. Pretty-printed body, headers, status. Click Try it:

The same shape via the operator CLI:

$hearth codex get dark-and-darker id.item.longsword_5001

And the entity's icon, rendered live from the response:

Longsword
mdc
::codex-icon{hash="22cd8c50c8829958106150b86dc5a436567a01df7015404f213669c5675b5b39" alt="Longsword"}
::

For the bare <img> form (and <picture> with WebP negotiation, CDN-resized variants, etc.) see Assets.

The pattern is consistent: fetch entities over HTTP, load images from their hash-addressed asset URLs, and reach for the CLI for batch operations or terminal-friendly output. Because every URL is content-addressed, a client can cache an icon forever — the bytes won't change without the URL changing.

How data flows

A patch goes from upstream game depots to a queryable HTTP endpoint in five stages:

flowchart LR
   depots(["upstream game depots"])
   extract["extract<br/>fetch · usmap · decode"]
   adapt["adapt<br/>per-game normalizer"]
   importer["import<br/>codex:import"]
   db[("codex DB<br/>Postgres")]
   http["/v1/codex HTTP API"]
   sdk["SDK · hearth CLI"]

   depots --> extract --> adapt --> importer --> db --> http --> sdk

   classDef ext fill:#1f1208,stroke:#7c2d12,color:#fdba74
   classDef hot fill:#0f1f0f,stroke:#16a34a,color:#86efac
   class extract,adapt,importer ext
   class db,http hot
StageWhere it runsWhat it produces
extractper-game container (e.g. dark-and-darker)paks → JSON + PNG + locres dumps on disk
adapthearth api containerone envelope JSON per patch (entities, assets, locales)
importhearth api containerrows in codex.entities, files in var/codex-assets/, rows in codex.locale_strings
HTTPhearth api container/v1/codex/{game}/{id}
SDKclienttyped accessors over the HTTP surface

Operators drive all five with one CLI: see the hearth codex CLI.

Three principles

These show up everywhere downstream — easier to spot once they're named.

Snapshot on change. A row is written when an entity's content_hash differs from the prior row on the same branch. A typical patch ships ~20k definitions but mutates a few hundred — only those few hundred are written. Re-importing the same patch is always a no-op: added=0 changed=0 removed=0 unchanged=N.

Content-addressed assets. The SHA-256 of the bytes IS the row id. Same icon across patches → same hash → one row, one file on disk, one URL. Asset URLs are immutable per hash, so clients can ship them with Cache-Control: public, max-age=31536000, immutable and never revalidate.

Read-only over HTTP. There is no HTTP write endpoint. Imports run via CLI inside the api container, so production reads stay isolated from the bulk-write path.

Where to go next

You want to ...Page
Understand the id grammar (id.item.longsword_5001)Identifiers
Understand what a row looks like, links, hydrateEntities
Render an icon, content addressing, CDN hintsAssets
Snapshot-on-change, diffs between patchesPatches & diffs
The Postgres tables behind itSchema
Request / response shapes, "Try it" panelsHTTP API reference
Use it from the browser / NodeJS SDK reference
Use it from inside the api repo (Symfony)PHP SDK reference
Drive the pipeline from a terminalhearth codex CLI
Run the extractor for Dark and DarkerDark and Darker pipeline