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.pngUse -o / --output to save to a specific path:
ccp storage get logo.png --output ./assets/logo.pngList 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.pngremove 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.
| Param | Effect |
|---|---|
?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=original | Bypass 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
| Subcommand | Aliases | Description |
|---|---|---|
create <name> | — | Create a new store |
put <files...> | upload | Upload one or more files |
get <filename> | — | Download a file (optional -o / --output) |
ls | list | List files in the store |
rm <filename> | remove | Delete 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 -aUseful 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.