Storage CLI

Last updated on Jul 12, 2026

Current Status

Storage has a first-class CLI command group in Sources/FirestructCLIKit/StorageCommand.swift. It reuses StorageService, so emulator behavior and guarded production URL/request shapes match the macOS Storage module. Approved live bucket verification is still required before live Storage hardening is claimed.

Project Selection

Commands use the selected project from project select unless --project <name|firebaseProjectID|uuid> is passed.

If --bucket is omitted, the CLI defaults to:

<firebaseProjectID>.appspot.com

Live Storage requests can pass either:

--authorization "Bearer <token>"
--access-token <token>

Commands

Bucket discovery:

storage buckets [--bucket fallback-bucket]

Object browsing:

storage list --bucket demo.appspot.com [--prefix avatars/] [--recursive] [--browser]
storage list --bucket demo.appspot.com --browser --sort-by updated --descending
storage inspect --bucket demo.appspot.com --path avatars/alice.png

--browser adds GUI-like folder/file entries built with StorageService.browserEntries. inspect returns object metadata, preview kind, metadata rows, and the object download URL.

Uploads:

storage upload --bucket demo.appspot.com --file ./avatar.png --path avatars/alice.png --confirm
storage upload --bucket demo.appspot.com --file ./data.json --path imports/data.json --content-type application/json --metadata '{"owner":"alice"}' --confirm
storage upload-folder --bucket demo.appspot.com --folder ./avatars --target avatars --confirm

Downloads:

storage download --bucket demo.appspot.com --path avatars/alice.png --output ./avatar.png
storage download-batch --bucket demo.appspot.com --paths avatars/a.png,avatars/b.png --output-dir ./downloads [--concurrency 4]
storage zip --bucket demo.appspot.com --paths avatars/a.png,avatars/b.png --output ./selection.zip
storage zip-folder --bucket demo.appspot.com --path avatars --output ./avatars.zip

Mutations:

storage delete --bucket demo.appspot.com --path avatars/alice.png --confirm
storage delete --bucket demo.appspot.com --paths avatars/a.png,avatars/b.png --confirm
storage delete-folder --bucket demo.appspot.com --path avatars --confirm
storage copy --bucket demo.appspot.com --path avatars/a.png --target avatars/b.png --confirm
storage move --bucket demo.appspot.com --path avatars/a.png --target archive/a.png --confirm
storage copy-folder --bucket demo.appspot.com --path avatars --target avatars-copy --confirm
storage move-folder --bucket demo.appspot.com --path avatars --target archive/avatars --confirm
storage rename-folder --bucket demo.appspot.com --path avatars --name archived-avatars --confirm
storage duplicate-folder --bucket demo.appspot.com --path avatars --confirm

URL helpers:

storage url --bucket demo.appspot.com --path avatars/alice.png
storage signed-url --bucket demo.appspot.com --path avatars/alice.png --expires 3600
storage signed-url --bucket demo.appspot.com --path avatars/alice.png --expires 3600 --service-account-file ./service-account.json

For emulator projects, signed-url returns a deterministic emulator download URL with the requested expiration. For production projects, V4 signing requires --service-account-file.

Safety

  • Upload, delete, copy, move, folder upload, folder delete, folder copy, folder move, folder rename, and folder duplicate require --confirm.
  • Read-only project links reject Storage mutations.
  • Folder copy/move checks for nested targets and duplicate destination objects before mutating.
  • Object copy/move validates that source and target paths differ.
  • Long multi-object operations run through TaskOrchestrator and return task status, counters, duration, and errors in JSON output.

Output

All commands emit JSON. Long-running commands include a task object:

{
  "status": "success",
  "processedOperations": 2,
  "totalOperations": 2,
  "attemptCount": 1
}

Current Verification

FirestructCLIIntegrationTests/testCLICommandsRoundTripAgainstFirebaseEmulators uploads two real Storage emulator objects, lists them through selected-module browser output, inspects and downloads one object, builds both selected-object and folder ZIP archives, validates archive payload bytes with /usr/bin/unzip, copies the folder, moves the copied folder, lists emulator source/target paths afterward, deletes an object, and verifies Storage folder/delete task titles are retained by tasks list.

FirestructCLIIntegrationTests/testStorageMoveSignedURLAndReadOnlyGuard verifies read-only project links reject Storage mutations and emulator signed URLs are deterministic. Approved live bucket verification is still required before live Storage hardening is claimed.

Source Anchors

  • Sources/FirestructCLIKit/StorageCommand.swift
  • Sources/FirestructCLIKit/FirestructCLI.swift
  • Sources/FirestructCLIKit/JSONOutput.swift
  • Sources/FirestructCore/StorageService.swift
  • Tests/FirestructCLIIntegrationTests/FirestructCLIIntegrationTests.swift