Assets

Immutable asset hosting

Upload once, reference forever. Every object is addressed by the hash of its contents, so a URL that worked yesterday returns exactly the same bytes tomorrow.

Addressing

An object lives at /o/<sha256>/<filename>. The filename is cosmetic — it shapes the Content-Disposition header and nothing else. Because the path contains the digest, we serve everything with Cache-Control: public, max-age=31536000, immutable and never need to invalidate an edge.

GET /o/3f9a1c07e5b24d8916ac0f5321bd7e4408cc91f2a6de3b70154e8829cf6a41db/logo.svg

Image transforms

query parameters, applied in order
ParameterMeaningDefault
w, htarget box in pixels, aspect preservedsource size
fitcontain · cover · padcontain
fmoutput format: avif · webp · jpeg · pngnegotiated
qquality, 1–10082
dprdevice pixel ratio multiplier, 1–31

Transforms are computed once and cached against the same immutable key, so the second request for a given combination is a straight edge hit. We do not accept arbitrary dimensions: values are snapped to the nearest step of 16 to keep the variant count sane.

Limits

SubjectLimit
Object size64 MB
Source image dimensions12 000 × 12 000
Upload rate, per token120 / minute
Retentionindefinite

Uploading

Uploads are a single PUT with the digest you expect. If the digest we compute does not match the one you sent, the write is rejected and nothing is stored — there is no partially written state to clean up afterwards.

Objects are never overwritten. Publishing a new version means publishing a new digest and changing the reference, which is the whole point: a deploy that rolls back gets its old assets back untouched.

Deleting

You can delete an object, and we will stop serving it within a minute across every edge. We deliberately make this the rare path rather than the routine one — if you find yourself deleting often, you are using mutable names somewhere upstream.