Docs/Codex/Assets

Assets

Icons, portraits, audio clips, and other binary assets are content-addressed:

flowchart LR
   bytes([PNG bytes]) -->|sha-256| hash["hash<br/>22cd8c50…"]
   hash --> row[("codex.assets row<br/>hash media_type bytes")]
   hash --> file["var/codex-assets/22/22cd8c50….png"]
   hash --> url["/v1/codex/assets/22cd8c50…"]
   classDef val fill:#0f172a,stroke:#334155,color:#e5e7eb
   classDef key fill:#1f1208,stroke:#7c2d12,color:#fdba74
   class hash key
   class row,file,url val

The SHA-256 of the bytes IS the row id. Same icon across patches → same hash → one row, one file on disk, one URL. URLs are immutable — clients can cache /v1/codex/assets/<hash> with Cache-Control: public, max-age=31536000, immutable and never revalidate.

Multi-asset entities

Entities reference assets through icon (the primary) and an assets array for multi-slot entities. A merchant carries one icon plus several portraits:

jsonc
{
   "id": "id.merchant.cockatrice",
   "icon":   { "hash": "abc123…", "url": "/v1/codex/assets/abc123…" },
   "assets": [
      { "role": "icon",     "hash": "abc123…", "media_type": "image/png", "bytes":  8868, "url": "/v1/codex/assets/abc123…" },
      { "role": "portrait", "hash": "def456…", "media_type": "image/png", "bytes": 18204, "url": "/v1/codex/assets/def456…" },
      { "role": "portrait", "hash": "ghi789…", "media_type": "image/png", "bytes": 19512, "url": "/v1/codex/assets/ghi789…" }
   ]
}

CDN-side resize / format hints (?size=256x256, ?format=webp) layer on top of the stable URL — the API itself always serves the original bytes.

Loading an icon in HTML

The url field is origin-relative. Prepend https://api.katforge.com (or whichever origin the SDK is configured against) before using it as an <img src>. This is the most common footgun — a bare url won't load against the docs site.

The same Longsword icon, rendered straight from the API (loads from whichever NUXT_PUBLIC_API_URL is configured for this build):

Longsword

The exact markdown above:

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

<CodexIcon> is an internal docs helper — it reads useRuntimeConfig().public.apiUrl and constructs the full URL at render time, so the same markdown loads from api.dev.katforge.com during local dev and api.katforge.com once the codex routes ship to prod. It's not part of the SDK; consumers integrating the codex into their own site should use the plain HTML below.

The HTML behind that — a bare <img> for prototypes, a <picture> element when production wants format negotiation:

HTML
<!-- Bare img -->
<img src="https://api.katforge.com/v1/codex/assets/22cd8c50c8829958106150b86dc5a436567a01df7015404f213669c5675b5b39"
     alt="Longsword"
     width="64" height="64" />

<!-- Picture element with WebP fallback and CDN-resized variants -->
<picture>
   <source type="image/webp"
           srcset="https://api.katforge.com/v1/codex/assets/22cd8c50c8829958106150b86dc5a436567a01df7015404f213669c5675b5b39?format=webp&size=128x128 1x,
                   https://api.katforge.com/v1/codex/assets/22cd8c50c8829958106150b86dc5a436567a01df7015404f213669c5675b5b39?format=webp&size=256x256 2x" />
   <img src="https://api.katforge.com/v1/codex/assets/22cd8c50c8829958106150b86dc5a436567a01df7015404f213669c5675b5b39?size=128x128"
        alt="Longsword" width="64" height="64" loading="lazy" />
</picture>

To wire icons up from a fetched entity, see Entities → loading URLs — the icon.url field is the same field shown above, just dynamic.

From the operator CLI

Resolve an entity's icon URL, all assets, or save bytes:

$hearth codex icon dark-and-darker id.item.longsword_5001
$hearth codex icon dark-and-darker id.merchant.cockatrice --all
$hearth codex icon dark-and-darker id.merchant.cockatrice --role portrait

From the SDKs

PHP
// PHP: $entity->icon () returns an Asset; ->url () builds the path with optional CDN hints.
$sword = $codex->dnd ()->item ('id.item.longsword_5001');
$sword->icon ()?->url ();                                  // /v1/codex/assets/22cd8c50…
$sword->icon ()?->url (size: '256x256');                   // …?size=256x256
$sword->icon ()?->url (size: '256x256', format: 'webp');   // …?size=256x256&format=webp

// Multi-asset entities:
$cock = $codex->dnd ()->merchant ('id.merchant.cockatrice');
foreach ($cock->assetList () as $asset) {
   echo $asset->url () . "\n";
}
TypeScript
// JS: the icon lives at icon.url; prepend the configured baseUrl.
const sword = await katforge.codex.game ('dark-and-darker').entity ('id.item.longsword_5001');
const src   = `${ katforge.apiBase }${ sword.icon.url }`;

See the PHP SDK reference and JS SDK reference for the full surface.

HTTP

The asset endpoint itself is documented in the HTTP API reference.