Skip to content

Media pipeline

The portal media pipeline converts raw uploaded images into optimized WebP variants, stores them in the GitHub repository, and serves them through a Netlify CDN-cached API route. Understanding this pipeline is useful when debugging media display issues or adding images manually.

Images are processed by the @drawnagency/authoring CLI. Run it from the client repo root:

Terminal window
pnpm exec authoring process-images --project .

The command reads raw images from _ingest/images/ and produces output in assets/images/. For each source image it generates WebP variants at each width in media.sizes (default: [640, 1080, 1920], configured in site-config.json).

Each processed image is assigned a 16-character hash ID derived from its content. The manifest file src/content/image-manifest.json maps each hash ID to its output folder and metadata (original filename, dimensions, MIME type). Section JSON files reference images by their hash ID in the imageId field.

Raw images go into _ingest/images/ before processing. This directory is gitignored — only the processed output in assets/images/ gets committed to the repository.

When the /populate-site skill runs, it downloads images from websites directly into _ingest/images/. Images embedded in PDFs (logos, label artwork, pattern graphics) cannot be extracted automatically; you must provide them manually by copying the files into _ingest/images/ and re-running process-images.

Processed images are committed to the GitHub repository as regular files. The portal serves them through /api/media/{imageId}/{width}.webp.

That API route:

  1. Resolves the imageId to a file path using the in-memory manifest cache (loaded once per function cold start).
  2. Fetches the WebP file from GitHub using the GitHub App installation token — not the GITHUB_TOKEN PAT. This means the images are fetched server-side and the GitHub repo can remain private.
  3. Returns the image with Netlify-CDN-Cache-Control: public, durable, immutable so Netlify’s CDN caches it at the edge. After the first request, subsequent requests for the same image are served from the CDN without hitting the function.

Because the URL contains a content-hash ID, cached responses remain valid until the image is replaced with a new file (which produces a new hash and a new URL).

To add an image outside of the /populate-site workflow:

  1. Drop the source file into _ingest/images/.
  2. Run pnpm exec authoring process-images --project .
  3. Note the hash ID assigned to the image in src/content/image-manifest.json.
  4. Reference the hash ID in your section JSON as "imageId": "<hash>".
  5. Commit assets/images/, src/content/image-manifest.json, and any updated section files.