# Upload and use images and files

> For Jenks — add photos, video clips and PDFs to the media library (Cloudflare R2) and use them as cover images, galleries, proof files or inline images, from the console, CLI, MCP or REST.

## What the media library is

Uploaded files live in a Cloudflare R2 bucket and are served from `https://jenksguo.xyz/media/…`.
Supported: WebP, JPEG, PNG, GIF, AVIF and SVG images, MP4 and WebM video, and PDF — up to 25 MB each.
The real type is read from the file itself, so a renamed file cannot pretend to be an image.

Every file gets a permanent address such as `/media/2026/10/acme-workshop-3f9a1c2b7d.webp`. The last
part is a fingerprint of the bytes, so an address never points at different content and browsers can
cache it for a year. Uploading the same file with the same name again reuses the existing copy.

**Files are public as soon as they are uploaded**, even before any page uses them. Upload only what
every visitor may see — never résumés with phone numbers, IDs or private documents.

## In the console

1. Open `https://jenksguo.xyz/admin` and choose **Media** in the top bar.
2. Drag files onto the page, paste a screenshot, or choose **Upload**. With **Optimise photos** on
   (the default), photos are resized to 2400 px and converted to WebP in your browser first — a 6 MB
   phone photo usually becomes 300–600 KB. iPhone HEIC photos are converted when the browser can read
   them (Safari can).
3. Select a file to add **alt text** — one sentence about what the image shows. Screen readers and
   the Ask Jenks assistant use it.

Then use files from the **Content** editor:

- **Cover image** — in *Details*, choose *Choose or upload a cover image*. It appears at the top of
  the entry page.
- **Gallery** — *Add photo or video* adds items shown beside the write-up, each with alt text and an
  optional caption.
- **Proof file** — in a proof row, the paperclip attaches an uploaded PDF (for example a paper).
- **Inside the text** — the image button in the editor toolbar inserts an image; you can also drag
  or paste an image straight into the text. Select an inserted image to edit its alt text.

Saving the entry publishes the change as usual. The editor refuses to save content that points at a
`/media/…` file that does not exist.

## From the CLI

```bash
jenks admin media upload ~/Pictures/acme-workshop.jpg --alt "Jenks running an AI workshop at Acme"
jenks admin media upload https://example.com/talk-poster.png     # the server fetches it
jenks admin media ls                                            # newest first, with "used by"
jenks admin media alt 2026/10/acme-workshop-3f9a1c2b7d.jpg "New alt text"
jenks admin media rm 2026/10/acme-workshop-3f9a1c2b7d.jpg
```

The CLI uploads files as they are (no resizing), so export large photos at a sensible size first.
Then reference the printed url in the entry:

```yaml
image: /media/2026/10/acme-workshop-3f9a1c2b7d.jpg
imageAlt: Jenks running an AI workshop at Acme
gallery:
  - src: /media/2026/10/acme-team-0c1d2e3f4a.webp
    alt: The Acme leadership team at the kick-off
    caption: Kick-off, October 2026
```

## From an AI agent (MCP)

The admin MCP server has `admin_upload_media`, `admin_list_media`, `admin_update_media` and
`admin_delete_media`.

- **Local** (`jenks mcp --admin`): pass `path` with a file on your computer — the local server reads
  it for the agent.
- **Remote** (`/mcp/admin`): pass `url` (an https address to import) or `data` (base64) with a
  `filename`.

A typical request to an agent: *"Upload ~/Desktop/xero-talk.jpg with alt text 'Jenks speaking at
Xerocon', then make it the cover image of xero-developer-evangelist."*

## From REST

```bash
curl -H "Authorization: Bearer $JENKSGUO_ADMIN_TOKEN" \
  -F file=@acme-workshop.jpg -F alt="Jenks running an AI workshop at Acme" \
  https://jenksguo.xyz/api/admin/media
```

`GET`, `PATCH` and `DELETE` on `/api/admin/media` list, update and delete. See the
[admin API reference](/agents/docs/reference/admin-api).

## Delete safely

Deleting is permanent: unlike content, files have no git history. The console and API refuse to
delete a file that published content still uses, and show where it is used. Remove the references
first, or force the delete if you accept broken images.

The *used by* check reads the content this environment last deployed. **dev and production share
one media library** (so files uploaded while previewing on dev keep working after you promote), so
a file used only by unpromoted dev content can look unused in the production console.
