Skip to main content
View as Markdown

Presets & Caching

Two concerns that sit behind the resize and format parameters: giving a recurring combination a name, and making sure the second request for it costs nothing.

Named Presets

A preset bundles width, height, fit, quality and format under one name. It is operator configuration — a JSON object in an environment variable, not app schema — because the right thumbnail size is a property of the deployment, not of the business domain.

>_ terminal
STORAGE_TRANSFORM_PRESETS='{
  "thumbnail": { "width": 150, "height": 150, "fit": "inside" },
  "preview":   { "width": 600, "quality": 80 },
  "avatar":    { "width": 64, "height": 64, "fit": "inside" }
}'
request.http
GET /api/buckets/photos/files/{key}?preset=avatar
GET /api/buckets/photos/files/{key}?preset=thumbnail&quality=95
Situation Result
Known preset Its fields become the transform.
Explicit parameter alongside a preset The explicit value wins, field by field.
Unknown preset name 400
?preset= with none configured 400, with a message saying so.

Overrides are per-field, not all-or-nothing: ?preset=thumbnail&quality=95 keeps the preset's 150×150 inside and changes only the quality.

Preset names must be alphanumeric with single hyphens (thumbnail, hero-banner), which keeps them safe to put in a URL. Malformed JSON, a non-object value, an illegal name, or a preset declaring the withdrawn crop key fails the server at startup rather than at the first request — a typo in this variable is found on deploy, not by a user.

The crop key is refused rather than ignored on purpose. Unknown keys used to be dropped without comment, so a preset carrying crop would have kept booting while quietly no longer cropping — see Resize & Fit for why the capability was withdrawn.

Caching

A transform runs once per distinct combination. The result is held in a process-local LRU cache keyed by the storage key, the transform parameters, and the negotiated format together.

Mechanism Behaviour
Cache-Control public, max-age=31536000, immutable — one year, never revalidated.
ETag Derived from the source file plus the transform parameters.
If-None-Match A match returns 304 Not Modified.
Server cache In-memory LRU, evicting least-recently-used entries when it is full.
Invalidation Deleting a file evicts every transform derived from its key.
Variable Default Meaning
STORAGE_TRANSFORM_CACHE_MAX_SIZE 256 Cache ceiling, in megabytes.

An entry larger than the whole cap is served but never retained — it would evict everything else to fit.

The one-year immutable header is safe precisely because keys are content-addressed — a stored key is <uuid>-<filename>, so a replaced file is a new key and a new URL. Nothing is ever served stale under a URL whose content changed, because that situation cannot arise.

Originals Are Never Touched

Guarantee Behaviour
Source bytes Byte-identical after any number of transform requests.
A request with no parameters The original, subject only to Accept negotiation.
?format=origin The original bytes verbatim, negotiation bypassed.
Clearing the cache Discards derived variants only; originals stay fully available.
Non-image files Transform parameters answer 400 — nothing is attempted.

Transforms are derived, disposable, and reproducible: the cache can be dropped at any moment and the next request rebuilds it. Admins can do exactly that with DELETE /api/admin/storage/transform-cache.

Recognised image types are those Sovrium can infer from the key's extension: PNG, JPEG, GIF, WebP, AVIF, SVG and ICO. SVG passes through rather than being rasterised, and is always served as an attachment — see Upload Security.

Last updated September 1, 2026

This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.

Built with Sovrium