Documentation Menu

Files and media

5 min read

A file in Quillet belongs to the whole project rather than to one environment, its bytes never change once uploaded, and it is public unless you mark it private.

Why files are shared and their bytes fixed

A logo is the same file in prod and in staging. If files belonged to an environment, every clone and every promotion would copy the bytes, and two copies of one picture could drift apart. So while everything else in Quillet is addressed by project, environment, key and locale, a file is addressed by project and id alone. Only the documents that point at it belong to an environment.

The bytes are fixed for a similar reason. Bytes that never change can be cached for a year at the same URL, and two uploads of the same bytes can be one file. What you call a file, and how you describe it, are a different matter. Those are words you can edit at any time, and editing them moves neither the bytes nor the URL.

How a file is stored, served and resized

Upload once, point at it everywhere

Quillet reads an upload's type from its bytes, never from its name or from the type the browser claimed. A raster image loses its EXIF block on the way in, after its orientation is applied, because a phone photo's metadata says where and when it was taken. If you upload the same bytes again, you get back the file that already exists.

Content points at a file with { "$asset": "<id>" }, the way it points at a document with $ref. Delivery replaces the pointer with the file's details: its URL, type, size, dimensions, description and the widths it can be asked for. Those details are read at the moment a reader asks, so fixing a description reaches every page without a save or a publish.

A pointer can also carry a description and a caption of its own for that one use, or mark the image as decorative there. The file's own description stays the default for every other use.

Public and private

A public file is served with no key at /d/v1/<project>/assets/<id>, because a browser drawing an <img> sends none. A private file is served only against a signed link, and that link expires after six to twelve hours. The API mints the link when it hands out the file's details. Delivery hands a link only to a delivery key that holds the assets:private scope. Any other key gets the details with a url of null.

Sizes and formats on request

To get a scaled, re-encoded copy of a raster image, add ?w=640&f=webp to its URL. Widths come from a fixed list, 320 to 2560 by default, and the formats are webp, avif, jpeg and png. Each copy is made once and stored, and it is never larger than the original. An SVG or an animated GIF is always served as uploaded.

Folders, and what uses a file

Folders in Media are there to help you find a file. A site never sees them: content points at a file by its id, so moving the file to another folder changes nothing a reader gets. A file's Used by list names every document that points at it, in every environment. While that list is not empty, deleting the file is refused.

How to

Dashboard

  1. Open the Media tab and press Upload files.
  2. Drop a picture onto the dialog, choose a folder, keep Public, write a description and upload.
  3. Open the file. Its sheet shows the URL, the size, the dimensions and Used by.
  4. Choose the file in an image field of a document and save. The document now appears under Used by, and Delete is unavailable with the reason beside it.

API

Ask for a public file's headers, then for a smaller WebP copy of it:

curl -s -I 'http://localhost:3000/d/v1/<project>/assets/<id>'
curl -s -I 'http://localhost:3000/d/v1/<project>/assets/<id>?w=640&f=webp'

Neither request needs a key, because the file is public. The first answers Cache-Control: public, max-age=31536000, immutable. The second's ETag is the original's with the variant added.

Limits

  • A file cannot be shared between projects, and its bytes cannot be replaced. To change a file, upload the new one and point at it.
  • There is no media history, no image editing and no tags. A video is stored and served as uploaded.
  • A signed link works for anyone who holds it until it expires. One link cannot be revoked on its own; rotating the signing secret retires all of them at once.
  • Making a public file private stops the origin serving it at once, but a copy that a CDN or a browser already cached may stay readable for up to a year.
  • Every new file is public unless you choose private, and a project cannot change that default.
  • A private file cannot be placed in a Markdown document.
  • By default, one upload may be 25 MB and a project may hold 5 GB.

Last updated