On this page
  1. How to run it
  2. Flags
  3. Bucket resolution
  4. What it writes
  5. Exit codes
  6. See also

The cairn-media-seed CLI

cairn-media-seed seeds wrangler’s local R2 simulator with every media-library object from a deployed cairn site, so vite dev serves real media on every page with no deploy. It reads the committed media manifest (src/content/.cairn/media.json, the engine’s own convention), downloads each object from the deployed site, and writes it into local R2 state under the same content-addressed key the media route reads. Once seeded, the local design-iteration guide covers the loop this enables. An author edits a source file, and vite dev serves the real image on reload.

The package ships the command in its bin field, so an install puts it on the project’s path. Run it once before you start iterating, and again whenever the deployed media library gains new objects you want locally.

How to run it

npx cairn-media-seed --from https://your-site.com

The command reads local config from the working directory, so run it from the directory that holds wrangler.jsonc (or wrangler.toml) and src/content/.cairn/media.json. It writes into wrangler’s local state the same way wrangler r2 object put --local always has, so a later vite dev or wrangler dev in the same directory sees the objects it wrote.

For a site behind Cloudflare Access or another auth gate, pass the header the gate needs on every download:

npx cairn-media-seed --from https://staging.your-site.com \
  --header 'CF-Access-Client-Id: <id>' \
  --header 'CF-Access-Client-Secret: <secret>'

Flags

FlagRequiredWhat it does
--from <base-url>YesThe deployed site to download from. Each manifest entry’s public delivery URL, <base-url>/media/<slug>.<hash>.<ext>, is derived from this. A trailing slash is tolerated.
--header 'Name: value'No, repeatableForwarded on every download request. Repeat the flag for multiple headers; a later --header for the same name overwrites an earlier one.
--bucket <name>NoThe R2 bucket name to write into. Overrides the wrangler-config resolution below.

A missing --from, an unknown flag, a flag with no value, or a --header that isn’t Name: value all print the usage line to stderr and exit 2 before any network request runs.

Bucket resolution

The command needs the R2 bucket’s real name, not just its wrangler binding, since wrangler r2 object put addresses a bucket by name. An explicit --bucket always wins. Failing that, the command reads wrangler.jsonc or wrangler.toml’s r2_buckets entries and uses the sole entry’s bucket_name when there is exactly one. Any other shape, no config file, zero entries, several entries, or a single entry missing bucket_name, is an error naming --bucket as the fix.

What it writes

For each manifest row with a slug, hash, and ext, the command downloads <from>/media/<slug>.<hash>.<ext> and writes the response body into the resolved bucket at media/<hash[0:2]>/<hash>.<ext>, the content-addressed key the media route reads on every request. The command stores each object with the Content-Type its extension implies, so local reads serve the same header production does. Re-running the command overwrites each key with the same bytes, so it’s safe to run again after the deployed library gains new objects. A row missing any of those three fields is dropped rather than failing the run. The manifest reader applies the same tolerance elsewhere.

Exit codes

CodeMeaning
0Every manifest entry synced (or the manifest holds none).
1At least one entry failed to download or write. Each failure prints FAILED <slug>: <message> to stderr before the summary line.
2Bad flags, or the R2 bucket name could not be resolved. The message names the fix.

A clean run always prints a summary line to stdout: cairn-media-seed: <ok> synced, <failed> failed, of <total> manifest entries.

See also

Edit this page on GitHub(opens in a new tab)