Static assets

Content addressing, cache headers and the asset lookup.

Updated 2 min read

Everything under app/public is copied to .cataract/public at build time and served from the root. Each file is hashed and given a version marker:

/styles.css?v=be6a02a3da64

The URL stays readable and the file keeps its name, so source maps and relative references inside a stylesheet keep working.

Resolving an asset

use CataractAssets;

meta.icon = asset("/img/emblem.svg");

asset is generated from the build's digest table and compiles to a select — a jump table, not a runtime map lookup. An unknown path is returned unchanged.

CSS files at the top level of public/ are linked into every page automatically. Nested stylesheets are not, so a page can opt into one through meta.stylesheets.

Cache behaviour

RequestResponse
Carries ?v=Cache-Control: public, max-age=31536000, immutable
No version markermax-age=<cache_seconds>
Any fileA content-derived ETag, stable across restarts and replicas
If-None-Match matches304 Not Modified

A versioned URL names one exact byte sequence, which is what makes the immutable directive truthful rather than optimistic.

Limits

Files are read whole into memory, so one larger than 8 MB is not served and a warning is logged. Served files are cached up to 32 MB; past that they are still served but re-read per request. cataract dev disables the cache so an edited file shows up on the next request.

Range requests are not supported.

The static layer re-checks paths

Before touching disk, the static file middleware independently refuses any path containing .., a backslash, a NUL, or a segment beginning with ., and never serves a directory. The request parser has already normalised the path — this is a second, independent guard, and either one alone would be sufficient.