# Run402 CLI -- Agent Reference — Assets, sites, domains, email, AI > Slice `assets` of the Run402 CLI reference. Index (start here): https://docs.run402.com/llms-cli.txt · Whole reference: https://docs.run402.com/llms-cli-full.txt ### assets (primary storage API) Direct-to-S3 asset storage, 1 byte to 5 TiB. Flat key namespace per project. Bulk directories: use `deploy` with `assets` slice: additive `assets: { put: [...] }`; declarative sync `assets: { put: [...], sync: { prefix, prune: true, confirm? } }`. No `run402 assets sync`; apply is canonical so HTML + asset URLs stage atomically. - `run402 assets put [files...] [--project ] [--key ] [--content-type ] [--private] [--immutable] [--meta ] [--exif-policy keep|strip] [--concurrency N] [--no-resume] [--stream]` — without `--stream`, stdout is the final results array (JSON). With `--stream`, stdout is NDJSON per-file progress events. `--json` is a deprecated alias for `--stream` (writes a deprecation warning to stderr). Stdout carries the canonical snake_case wire shape only (see the `assets put` output example below); the SDK's camelCase `AssetRef` conveniences never appear on CLI stdout. - `run402 assets get --output [--project ]` - `run402 assets ls [--project ] [--prefix

] [--limit ] [--sort key:asc|createdAt:asc|createdAt:desc] [--filter ...]` - `run402 assets rm [--project ]` - `run402 assets sign [--project ] [--ttl ]` — signed URL TTL must be an integer from 60 to 604800 seconds. - `run402 assets diagnose [--project ]` — inspect live CDN state for a public URL - `run402 cdn wait-fresh --sha [--timeout ] [--project ]` — poll a mutable URL until it serves the expected SHA-256 Project resolution for every command above: `--project ` > `RUN402_PROJECT_ID` (the canonical env var, same as every other project-scoped command) > the deprecated `RUN402_PROJECT` alias > the active project. `RUN402_PROJECT` works as a fallback but prints one deprecation line to stderr; switch to `RUN402_PROJECT_ID`. `put` flags: - `--meta key=value` repeatable; coercion: numeric-looking -> number, `true|false` -> boolean, comma -> `string[]`, else string. Serialized total <=4 KB; no nested objects; invalid -> `INVALID_ASSET_METADATA`. - `--exif-policy keep|strip`; default `keep`; `strip` removes EXIF bytes + `image_exif`; invalid -> `INVALID_EXIF_POLICY`. `ls` flags: - `--sort key:asc|createdAt:asc|createdAt:desc` — result ordering. Default `key:asc` (legacy bare-key cursor). The `createdAt:*` variants use a base64url JSON `{s, ts, key}` cursor; reusing a cursor across sort keys returns `400 INVALID_CURSOR_FOR_SORT`. - `--filter key=value` — repeatable media-picker filter. Allowed keys: `uploaded_by`, `tag`, `format`, `is_image` (`true`/`false`), `min_width`, `max_width`, `min_height`, `max_height` (non-negative ints). Unknown keys are rejected client-side with `INVALID_FILTER_KEY`. Examples: ``` run402 assets put ./artifact.tgz --project prj_abc123 run402 assets put ./hero.jpg --project prj_abc123 --meta uploaded_by=agent_abc --meta version=3 --meta tags=hero,banner --exif-policy strip run402 assets put ./dist/**/*.png --project prj_abc123 --key assets/ run402 assets put ./asset --project prj_abc123 --key assets/logo --content-type image/svg+xml run402 assets put huge.bin --project prj_abc123 --immutable run402 assets get images/logo.png --output /tmp/logo.png --project prj_abc123 run402 assets ls --project prj_abc123 --prefix images/ run402 assets ls --project prj_abc123 --sort createdAt:desc --filter is_image=true --filter min_width=320 --filter format=webp run402 assets ls --project prj_abc123 --filter uploaded_by=agent_abc --filter tag=hero run402 assets diagnose https://app.run402.com/_blob/avatar.png --project prj_abc123 run402 cdn wait-fresh https://app.run402.com/_blob/avatar.png --sha ba78... --timeout 120 ``` CLI `assets put` output (canonical snake_case wire shape — one entry per file, plus the local `file` path): ```json [ { "file": "./hero.png", "key": "hero.png", "sha256": "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "size_bytes": 1234, "content_type": "image/png", "visibility": "public", "immutable": true, "url": "https://pr-abc.run402.com/_blob/hero.png", "immutable_url": "https://pr-abc.run402.com/_blob/hero-ba7816bf.png", "cdn_url": "https://pr-abc.run402.com/_blob/hero.png", "cdn_immutable_url": "https://pr-abc.run402.com/_blob/hero-ba7816bf.png", "sri": "sha256-unhbz…", "etag": "\"sha256-ba7816bf…\"", "content_digest": "sha-256=:unhbz…:", "metadata": null, "image_format": "png", "image_info": { "has_alpha": true }, "image_exif": null, "image_exif_policy": "keep" } ] ``` Image uploads additionally carry the snake_case image fields (`width_px`, `height_px`, `blurhash`, `variant_spec_version`, `display_url`, `display_immutable_url`, `variants`, `blurhash_data_url`, `asset_schema`). The SDK's typed `AssetRef` (below) exposes camelCase conveniences (`cdnUrl`, `immutableUrl`, `contentSha256`, `size`, …) whose values are guaranteed identical to their snake_case wire twins; the CLI emits the wire shape only. SDK `put` response (`AssetRef`): ```js const asset = await client.assets.put(projectId, key, { bytes }); // defaults to immutable: true html += asset.scriptTag(); // html += asset.linkTag(); // html += asset.imgTag("Company logo"); // Company logo html += asset.imgTagWithSrcSet({ alt: "Hero", sizes: "(max-width: 800px) 100vw, 1920px" }); // → // Hero ``` `immutable: true` is the default. SDK/CLI compute SHA-256; gateway returns content-addressed URL + SRI. Immutable URL needs no invalidation/redeploy fix/`cdn wait-fresh`. Use `{ immutable: false }` only for mutable URL/cache semantics; tag emitters throw without immutable URL/SRI. Emitters include `defer`, `loading="lazy"`, `decoding="async"`. AssetRef fields: - `cdnUrl` — content-addressed emitter URL, `https://pr-.run402.com/_blob/-<8hex>.`, served through the CDN, guaranteed reachable. - `cdnMutableUrl` — mutable auto-subdomain URL; eventual consistency; prefer `cdnUrl`. - `url` / `immutableUrl` — preferred-host forms on claimed/custom domain; currently not CDN-served; use for direct API consumers, not `