---
summary: S3-compatible storage with API-proxied URLs and image variants.
title: Assets
path: concepts/assets
status: published
---

# Assets

Asset uploads land in S3-compatible storage (MinIO in dev, AWS S3 or any
compatible service in production). Each upload gets:

- A canonical `storage_key`.
- Variants for images (`thumbnail`, `small`, `medium`, `large`) generated
  by a background task.
- An API-proxied URL: `/api/v1/assets/{asset_id}/file`.

The proxy URL is what you embed in content — no direct S3 access from
clients, no signed-URL renewal headaches, and revoking access is one
flag flip.

## Asset metadata

Each asset carries:

- `mime_type`, `size_bytes`
- `original_filename`
- Optional `alt_text` (translatable)
- Optional `tags`
- A `folder` for organisation (just a virtual path)

## Variants

For images, the backend kicks off a sharp-style resize per
`asset_image_variants` setting. Defaults:

```python
asset_image_variants = {
    "thumbnail": {"width": 150, "height": 150, "fit": "cover"},
    "small":     {"width": 320, "height": 320, "fit": "inside"},
    "medium":    {"width": 800, "height": 800, "fit": "inside"},
    "large":     {"width": 1600, "height": 1600, "fit": "inside"},
}
```

Variants are stored as separate S3 objects and served through the same
`/file` proxy with `?variant=medium`.

## Allowed types

Configurable via `asset_allowed_mime_types`. Defaults include JPEG/PNG/GIF/
WebP/SVG, PDF/Word/Excel, MP4/WebM, MP3/WAV/OGG. Anything else is rejected
at upload.
