Commands
Each CLI command maps to an SDK method - upload, download, head, exists, delete, copy, move, list, search, url, sign-upload, transfer, sync, and capabilities.
Each command maps to a Files method. Same semantics, same FilesError codes, same StoredFile fields on the way out (emitted as flat JSON).
| Command | SDK method | What it does |
|---|---|---|
upload |
upload |
Write a file or piped stdin to a key |
download |
download |
Read a key to disk or stream it to stdout |
head |
head |
Fetch metadata without the body |
exists |
exists |
Test a key — prints { exists, key }, sets exit code |
list |
list |
Page through keys under a prefix |
search |
search |
Find keys matching a glob, regex, substring, or exact pattern |
copy |
copy |
Copy a key to a new key |
move |
move |
Move (rename) a key |
delete |
delete |
Delete one or more keys |
url |
url |
Get a read URL — presigned or public |
sign-upload |
signedUploadUrl |
Mint a browser-direct upload policy |
transfer |
transfer |
Stream every object to another provider |
sync |
sync |
Mirror onto another provider (skip-unchanged, prune) |
capabilities |
capabilities |
Print what the configured adapter can do, as JSON |
All examples use --provider s3 --bucket uploads; swap in any provider and its flags.
Methods
upload
Write a body to a key. Read it from a file with --file, or pipe it through --stdin. Without --content-type, the content type is inferred from the key’s extension (application/octet-stream when it has none).
files --provider s3 --bucket uploads \
upload reports/2026-q1.pdf --file ./report.pdf --content-type application/pdf
cat report.pdf | files --provider s3 --bucket uploads \
upload reports/2026-q1.pdf --stdin --content-type application/pdf
For large objects see multipart; to push a whole local tree see directories; for create-only or compare-and-set writes see conditional predicates.
download
Read a key back. --out writes it to a file; --stdout streams the raw bytes so you can pipe them onward.
files --provider s3 --bucket uploads download reports/2026-q1.pdf --out ./report.pdf
files --provider s3 --bucket uploads download reports/2026-q1.pdf --stdout > report.pdf
To pull a slice instead of the whole object see byte ranges; to fetch many keys at once see directories.
head
Fetch an object’s metadata — size, content type, etag, last-modified — without downloading the body.
files --provider s3 --bucket uploads head reports/2026-q1.pdf
Pass several keys to inspect them in one call; see many keys at once.
exists
Test whether a key exists. It prints { exists, key } and signals the result through the exit code too, so it drops straight into a shell conditional.
files --provider s3 --bucket uploads exists reports/2026-q1.pdf # exit 0 = exists, 1 = missing
With several keys it prints { existing, missing } instead and exits 0 only when every key exists. A hard failure (auth, transport) exits with its error’s own code rather than 1.
list
Return one page of keys under a prefix. --prefix filters this call (distinct from the instance-wide --key-prefix) and --limit caps the page.
files --provider s3 --bucket uploads list --prefix reports/ --limit 50
The result carries a cursor for the next page. Pass --all to follow the cursor to the end and return every item in one result — mind the memory cost on huge buckets:
files --provider s3 --bucket uploads list --prefix logs/ --all | jq '.items[].key'
Pass --delimiter to collapse keys into folders — the direct files come back in items and the subfolders in a prefixes array (full keys with the trailing delimiter). This is the building block for a file-browser view; it throws on adapters with no folder concept, and can’t be combined with --all:
files --provider s3 --bucket uploads list --prefix photos/ --delimiter / | jq '.prefixes'
# ["photos/2023/", "photos/2024/"]
search
Find keys matching a pattern, walking every page. <pattern> is a glob by default (* within a path segment, ** across /, ? one character); --match switches to regex, substring, or exact, and --regex is shorthand for --match regex. The result is { items } with no cursor.
files --provider s3 --bucket uploads search 'reports/**/*.pdf'
files --provider s3 --bucket uploads search '\.(png|jpe?g)$' --regex --prefix photos/ --max-results 20
A glob’s literal prefix (reports/ above) bounds the walk automatically. Regex, substring, exact, and --case-insensitive searches walk everything under --prefix, so pass one on a large bucket. --max-results stops after that many matches; --limit sets the page size of the underlying walk, not the number of results.
copy
Copy a key to a new key, leaving the source in place.
files --provider s3 --bucket uploads copy reports/2026-q1.pdf reports/archive/q1.pdf
move
Move (rename) a key — a native rename where the provider supports it, otherwise a copy followed by a delete of the source.
files --provider s3 --bucket uploads move uploads/tmp-q1.pdf reports/2026-q1.pdf
delete
Delete a key.
files --provider s3 --bucket uploads delete reports/archive/q1.pdf
Pass several keys to delete them in one fan-out; see many keys at once. To delete only a specific generation see conditional predicates.
url
Get a read URL for a key — presigned and short-lived on signing adapters, a public URL for CDN-backed providers. --expires-in sets the lifetime in seconds.
files --provider s3 --bucket uploads url reports/2026-q1.pdf --expires-in 600
sign-upload
Mint a presigned upload for browser-direct uploads. --expires-in is required. On S3, --max-size switches from a presigned PUT URL (no size limit) to a POST policy that enforces the size server-side, so the client can’t exceed it. Adapters that can’t enforce --max-size fail closed.
files --provider s3 --bucket uploads sign-upload uploads/avatar.png \
--expires-in 600 --max-size 5242880 --content-type image/png
capabilities
Print the configured adapter’s capability snapshot as JSON — range reads, native upload progress, list delimiters, metadata, cache-control, multipart, server-side copy, signed URLs, and conditional operations. Pure introspection; it makes no provider call.
files --provider s3 --bucket uploads capabilities
transfer
Stream every object from the configured (source) provider to another provider, given as a JSON config. The source uses the normal global flags (so --key-prefix scopes it); --prefix filters the walk, and --no-overwrite skips keys already present at the destination.
# Migrate an S3 prefix to R2, skipping anything already copied
files --provider s3 --bucket old --verbose \
transfer \
--to '{"provider":"r2","bucket":"new","accountId":"...","accessKeyId":"...","secretAccessKey":"..."}' \
--prefix uploads/ --no-overwrite --concurrency 16
sync
Mirror the source onto another provider: upload new or changed objects, skip the unchanged ones, and — with --prune — delete destination keys the source no longer has. --compare picks the change check (etag, the default, or size for cross-provider mirrors). Unlike every other command, --dry-run here lists both sides and prints the real reconciliation plan ({ uploaded, skipped, deleted }) without mutating anything — preview a --prune before you run it.
# Back up an S3 prefix to R2 — only the delta moves, and the backup mirrors deletes
files --provider s3 --bucket live --verbose \
sync \
--to '{"provider":"r2","bucket":"backup","accountId":"...","accessKeyId":"...","secretAccessKey":"..."}' \
--prefix uploads/ --prune --compare size --concurrency 16
# Preview what a pruning mirror would do, read-only
files --provider s3 --bucket live \
sync --to '{"provider":"r2","bucket":"backup",...}' --prune --dry-run
Global flags
These apply to every command and mirror the Files constructor and OperationOptions:
# --key-prefix scopes every operation under a base path (the instance prefix,
# distinct from `list --prefix`, which is a one-off filter). Listed/returned
# keys come back relative to it.
files --provider s3 --bucket uploads --key-prefix tenants/acme \
list # lists under tenants/acme/
# --timeout (per attempt, ms) and --retries (provider failures) apply to all commands
files --provider s3 --bucket uploads --timeout 10000 --retries 3 \
head reports/2026-q1.pdf
Many keys at once
head, exists, and delete take multiple keys and return a structured result instead of throwing on partial failure. --concurrency and --stop-on-error tune the fan-out:
files --provider s3 --bucket uploads head a.txt b.txt c.txt
files --provider s3 --bucket uploads delete a.txt b.txt --concurrency 16
files --provider s3 --bucket uploads exists a.txt b.txt --stop-on-error
Byte ranges and multipart
# Download a byte range (0-based, inclusive) — for video seeking or resuming.
# Range downloads throw on adapters with no native range primitive.
files --provider s3 --bucket uploads download big.mp4 --out head.mp4 --range 0-1048575
# Upload in parallel parts (robust for large objects). --part-size /
# --multipart-concurrency tune it and imply --multipart.
files --provider s3 --bucket uploads \
upload big.iso --file ./big.iso --multipart --part-size 16777216
Conditional predicates
Single-key upload, download, delete, and copy accept the SDK’s conditional predicates as flags. They fail closed on adapters without native support and have no bulk form.
# Create only: fail if the key already exists.
files --provider s3 --bucket uploads upload config.json --file ./config.json --if-none-match
# Replace, read, or delete only the generation with this ETag.
files --provider s3 --bucket uploads upload config.json --file ./config.json --if-match a1b2c3
files --provider s3 --bucket uploads download config.json --out ./config.json --if-match a1b2c3
files --provider s3 --bucket uploads delete config.json --if-match a1b2c3
# Conditional copy checks the source ETag and the destination in one request:
# --if-match guards the source; --if-none-match (create) or --dest-if-match
# (replace that generation) guards the destination. Both halves are required.
files --provider s3 --bucket uploads copy staging/a.json published/a.json --if-match a1b2c3 --if-none-match
files --provider s3 --bucket uploads copy staging/a.json published/a.json --if-match a1b2c3 --dest-if-match d4e5f6
Pass the canonical bare strong ETag (no quotes, no W/), as printed by head, upload, and list. --dry-run echoes the resolved condition.
Directories
Upload a whole local tree, or download many keys into a directory. Each file is keyed by (or written to) its relative path; content types are inferred per file on upload.
# Upload every file under ./build, keyed by relative path (composes with --key-prefix)
files --provider s3 --bucket site --key-prefix assets upload --dir ./build
# Download many keys into a directory, recreating their key paths underneath it
files --provider s3 --bucket uploads \
download docs/a.pdf docs/b.pdf logos/c.png --out-dir ./pulled