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| Parameter | Meaning | Default |
|---|---|---|
w, h | target box in pixels, aspect preserved | source size |
fit | contain · cover · pad | contain |
fm | output format: avif · webp · jpeg · png | negotiated |
q | quality, 1–100 | 82 |
dpr | device pixel ratio multiplier, 1–3 | 1 |
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
| Subject | Limit |
|---|---|
| Object size | 64 MB |
| Source image dimensions | 12 000 × 12 000 |
| Upload rate, per token | 120 / minute |
| Retention | indefinite |
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.