## 1. MinIO Provisioning

- [x] 1.1 Compose + env on .13 (`wherewoof-minio`, API 9000 / console 9001, volume); `docker compose up -d`; create bucket `wherewoof`; verify API (mc or curl)

## 2. Go Frontend

- [x] 2.1 Add `github.com/minio/minio-go/v7`; config (`MINIO_ENDPOINT/ACCESS/SECRET/BUCKET`); client helper (no-op when unset)
- [x] 2.2 `UploadPhoto` → PutObject (`tags/{id}.{ext}`) → set `photo_url=/photos/tags/{id}.{ext}`; new `GET /photos/{key...}` handler streaming GetObject with content-type + cache headers; route in main.go
- [x] 2.3 Build + verify: upload via HTTP → object in MinIO → public page + `/photos/…` serve 200

## 3. Laravel Admin

- [x] 3.1 `composer require league/flysystem-aws-s3-v3`; `config/filesystems.php` `minio` disk (endpoint .13:9000, use_ssl false); `admin/.env` MINIO creds
- [x] 3.2 TagResource: photo `FileUpload` on the `minio` disk (path `tags/`), URL = `https://where-woof.com/photos/{path}`; verify upload from admin UI

## 4. Migration & Deploy

- [x] 4.1 One-off copy `static/uploads/*` into MinIO (matching keys)
- [x] 4.2 Deploy: frontend env (`MINIO_*` in ~/.config/where-woof.env) + rebuilt binary + restart; admin .env + rebuild container
- [x] 4.3 Live round-trip: upload a photo via where-woof.com → loads; via admin → loads
- [x] 4.4 `openspec validate photo-object-storage`; commit
