Storage

Upload, list, and manage static files on the Cluster edge CDN.

Storage

Cluster's object storage lets you keep static files (images, CSS, PDFs, etc.) on the edge CDN and serve them alongside your functions. Files live inside a store — a named bucket tied to your organization. You can have multiple stores per organization.

The command is available as ccp store (primary) or ccp storage (alias) — use whichever you prefer.

Create a Store

ccp storage create my-assets
# ✓ Created store "my-assets"

A store needs to exist before you can upload files to it. When you run any storage command, CCP will use the store_id from your project's .cluster/config.json if present, or prompt you to select one.

Upload Files

ccp storage put logo.png styles.css
# ◼ Uploaded logo.png
#   https://assets.cluster.app/edge/serve/...
#
# ◼ Uploaded styles.css
#   https://assets.cluster.app/edge/serve/...

Multiple files upload in parallel. upload is an alias for put if you prefer.

Download a File

ccp storage get logo.png
# ✓ Downloaded logo.png

Use -o / --output to save to a specific path:

ccp storage get logo.png --output ./assets/logo.png

List Files

ccp storage ls
# • logo.png    12.4 KB
#   https://assets.cluster.app/edge/serve/...
#
# • styles.css  3.2 KB
#   https://assets.cluster.app/edge/serve/...

list is an alias for ls.

Remove Files

ccp storage rm logo.png
# ✓ Removed logo.png

remove is an alias for rm.

Image Transformations

Files uploaded to a store are served through Cluster's edge with on-the-fly image processing. Append query parameters to the served URL to resize or change format — the result is computed once and cached, so repeat hits at the same parameters are cheap.

ParamEffect
?w=<px>Resize width. Height is auto-scaled to preserve aspect ratio.
?h=<px>Resize height. Width is auto-scaled.
?w=<px>&h=<px>Resize to exact dimensions.
?format=originalBypass conversion and serve the raw uploaded bytes.
<!-- Original encoding (default) -->
<img src="https://assets.cluster.app/edge/serve/me/photo.jpg">

<!-- WebP-encoded; explicit opt-in -->
<img src="https://assets.cluster.app/edge/serve/me/photo.jpg?format=webp">

<!-- Resized to 400px wide, original encoding -->
<img src="https://assets.cluster.app/edge/serve/me/photo.jpg?w=400">

<!-- Resized + WebP -->
<img src="https://assets.cluster.app/edge/serve/me/photo.jpg?w=400&format=webp">

<!-- Exact 200x200, original encoding -->
<img src="https://assets.cluster.app/edge/serve/me/photo.jpg?w=200&h=200">

Default: Original passthrough

By default, the served response returns the original uploaded bytes — same content-type, no transcoding. To opt into WebP encoding, pass ?format=webp explicitly. WebP is materially smaller than JPEG/PNG at equivalent quality on every modern browser, so it's the right choice for most image embeds — just opt in per-request rather than receiving it implicitly.

Non-image content (CSS, JS, PDF, JSON, etc.) is served as-is regardless of params — only supported image MIME types go through the processor. Resize parameters (?w=, ?h=) imply transcoding even if format isn't set, since the bytes change either way.

Caching

URLs uploaded via ccp store put are currently mutable: the same URL is served on every upload, and the response carries Cache-Control: no-store to keep callers correct on Cloud CDN, which over-retains mutable URLs even with short max-age values. Image transforms (?w=, ?h=, ?format=) are still computed once and cached at the origin (in-memory + GCS at _cache/{path}/{key}), but the client-side response itself is not CDN-cacheable — every request reaches origin.

A future CCP release will switch the default upload path to a content-addressed family that produces immutable URLs cacheable forever by Cloud CDN. The URL embeds a content hash ({store}/m/{hash}/{filename}), re-uploads with new bytes mint a new URL, and old URLs keep serving from GCS until lifecycle cleanup. See infra issue #109 for the migration plan.

Either way, sticking to a small canonical set of sizes (e.g. ?w=400, ?w=800, ?w=1600) is much cheaper than passing arbitrary widths per page — each unique tuple is a fresh transcode on the first hit.

Subcommand Reference

SubcommandAliasesDescription
create <name>Create a new store
put <files...>uploadUpload one or more files
get <filename>Download a file (optional -o / --output)
lslistList files in the store
rm <filename>removeDelete a file

Function Context

Storage commands need to know which store to operate on. CCP resolves this automatically if your project's .cluster/config.json has a store_id. Otherwise, it prompts you to select an organization and store interactively, and persists the chosen store_id back to .cluster/config.json so subsequent commands skip the prompt.

Bypass the pin: -a / --all

Pass -a or --all to any storage subcommand (put, get, ls, rm) to bypass the persisted store_id and re-prompt for store selection:

ccp storage ls --all       # list stores across all orgs, pick one for this command
ccp storage put logo.png -a

Useful when you need to operate on a store that isn't the one pinned to the current project, or in scripts where the pinned ID isn't appropriate. The flag is purely per-invocation — it doesn't update the persisted store_id.

On this page