CLI

sign

storage sign is a subcommand group:

storage sign download <path>   # signed GET URL (string)
storage sign upload <path>     # signed PUT/POST URL (JSON: method, url, fields?)

The split is intentional — download URLs are bare strings (shell-friendly: URL=$(storage sign download foo)), upload URLs carry structure (method + url, plus form fields for S3-style POST uploads) that needs JSON.

storage sign download <path>

storage sign download photos/cat.jpg
storage sign download photos/cat.jpg --ttl 3600
storage sign download photos/cat.jpg --snapshot snap-0193abc1234567890abcdef

Human mode prints a bare URL on stdout so $(storage sign download …) works in a script. JSON mode emits { "url": "…" }.

Flags

storage sign upload <path>

storage sign upload uploads/incoming.jpg
storage sign upload uploads/incoming.jpg --ttl 600 --content-type image/jpeg --max-size 5242880

Output is always JSON — the result shape varies by backend:

{ "method": "PUT", "url": "https://…?signature=…" }
{
  "method": "POST",
  "url": "https://…",
  "fields": {
    "key": "uploads/incoming.jpg",
    "Content-Type": "image/jpeg",
    "policy": "…",
    "x-amz-signature": "…"
  }
}

A PUT-style URL is used directly: curl -X PUT '<url>' --data-binary @local.jpg. A POST-style URL needs every field passed as a form field plus the file: curl -X POST '<url>' -F key=… -F policy=… -F file=@local.jpg.

Flags

--snapshot is rejected here — snapshots are read-only. Use --fork to target a fork.

Backend support

Adapters that don’t enforce a constraint (e.g. the local filesystem adapter) silently drop --content-type, --max-size, and --min-size. The signed URL is still returned, but the policy isn’t enforced. For real client-side upload policies, use an S3-compatible adapter or one that documents the constraints it honors.