@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_5001And the entity's icon, rendered live from the response:
::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| Stage | Where it runs | What it produces |
|---|---|---|
| extract | per-game container (e.g. dark-and-darker) | paks → JSON + PNG + locres dumps on disk |
| adapt | hearth api container | one envelope JSON per patch (entities, assets, locales) |
| import | hearth api container | rows in codex.entities, files in var/codex-assets/, rows in codex.locale_strings |
| HTTP | hearth api container | /v1/codex/{game}/{id} |
| SDK | client | typed 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, hydrate | Entities |
| Render an icon, content addressing, CDN hints | Assets |
| Snapshot-on-change, diffs between patches | Patches & diffs |
| The Postgres tables behind it | Schema |
| Request / response shapes, "Try it" panels | HTTP API reference |
| Use it from the browser / Node | JS SDK reference |
| Use it from inside the api repo (Symfony) | PHP SDK reference |
| Drive the pipeline from a terminal | hearth codex CLI |
| Run the extractor for Dark and Darker | Dark and Darker pipeline |