██
██
██ _ ____ _ _____
██ / \ | _ \ | | | ____|
██ / _ \ | | | || | | _|
██ / ___ \ | |_| || |___ | |___
▄██▄ /_/ \_\|____/ |_____||_____|
██████
▀████▀
Edit S3, Google Cloud Storage, and Azure Blob Storage files directly from your terminal. One command.
ladle s3://mybucket/config.json
ladle gs://mybucket/config.json
ladle az://mycontainer/config.jsonDownload, edit in your favorite editor, diff, confirm, upload — all in one shot. No manual download/upload, no web console, no scripts.
- One command to edit —
ladle s3://bucket/fileopens, edits, diffs, and uploads - Metadata too —
ladle --meta s3://bucket/fileedits ContentType, CacheControl, etc. as YAML - Any terminal editor — vim, emacs, nano — set
EDITORor--editor - Safe by default — colored diff + confirmation before every upload
- Pipe & redirect — works in shell pipelines and scripts with auto-detection
- Browse & filter — interactive TUI browser with vim-style
/search
$ ladle s3://myapp/config.json
Downloading s3://myapp/config.json ...
Temp file: /tmp/ladle-123/config.json
(your editor opens, you make changes, save and close)
File: s3://myapp/config.json
--- original
+++ modified
@@ -1,3 +1,3 @@
{
- "debug": false,
+ "debug": true,
"port": 8080
}
Upload changes? [y/N]: y
Uploading to s3://myapp/config.json ...
Done.
$ ladle --meta s3://myapp/index.html
Fetching metadata for s3://myapp/index.html ...
(editor opens with YAML)
# s3://myapp/index.html
ContentType: text/html
CacheControl: max-age=3600
Metadata:
author: alice
(change CacheControl, save and close)
--- original
+++ modified
@@ -2,3 +2,3 @@
ContentType: text/html
-CacheControl: max-age=3600
+CacheControl: max-age=86400
Metadata:
Update metadata? [y/N]: y
Done.
Metadata updates use the S3 CopyObject API — no re-upload of file content.
ladle detects shell redirection so you can use it in pipelines and scripts — no interactive editor needed.
# Download to local file
ladle s3://myapp/config.json > config.json
# Upload from local file (shows diff, asks confirmation)
ladle s3://myapp/config.json < config.json
# Skip confirmation
ladle --yes s3://myapp/config.json < config.json
# Preview changes without uploading
ladle --dry-run s3://myapp/config.json < config.json
# Export / import metadata as YAML
ladle --meta s3://myapp/index.html > meta.yaml
ladle --meta s3://myapp/index.html < meta.yaml
# Transform and re-upload
ladle s3://myapp/config.json | jq '.debug = true' | ladle --yes s3://myapp/config.json
# Append to the existing value instead of replacing it (creates the object if missing)
echo "$(date) deployed" | ladle --append --yes s3://myapp/deploy.log
# List objects/buckets (one URI per line; directories keep trailing /).
# Redirect or pipe so stdout is non-TTY — a bare terminal opens the TUI browser instead.
ladle s3://myapp/config/ > objects.txt # objects + subdirectories under config/
ladle s3:// | grep myapp # all buckets, as s3://<bucket>/
# List an object's versions (tab-separated: id, modified, size, latest, delete-marker)
ladle --versions s3://myapp/config.json > versions.tsvWhen stdout is redirected, a directory URI (or bare scheme) prints a listing and --versions prints version history, instead of opening the interactive TUI.
When stdin is redirected, confirmation reads from /dev/tty. Use --yes to skip in non-interactive environments. If the object doesn't exist yet, stdin upload creates it as a new object. Add --append to keep the existing value and add stdin after it (a missing object is still created).
ladle cp downloads the source completely before it changes the destination. It copies the body and standard metadata (Content-Type, Cache-Control, Content-Encoding, Content-Disposition, and user metadata). Provider-specific settings such as ACLs, storage class, tags, retention settings, and encryption keys are not copied.
# Preview content and metadata differences without writing
ladle cp --dry-run s3://myapp-source/config.json s3://myapp-destination/config.json
# Copy after approval
ladle cp --yes s3://myapp-source/config.json s3://myapp-destination/config.jsonThe source and destination may use different supported object-storage providers. Binary content is copied but its content diff is skipped. cp does not support ssm:// parameters.
$ ladle s3://myapp/
██ _ ___ _ ____
██ /_\ | _ \| | | __|
▄▄██▄ / _ \| | || |__| _|
██████_/ \_\___/|____|____|
▀██▀ v1.0.0
s3://myapp
> 📁 config/
📁 static/
📝 index.html 2.1 KB 2026-02-19 02:08
📝 readme.md 1.3 KB 2026-02-19 02:08
..
/ index▏
↑/↓ or wheel navigate ←/→ collapse/expand enter select space preview → menu - up n new / filter esc×2 quit
↑/↓or the mouse wheel / trackpad to navigate entries;←/→expand/collapse directories/to filter — incremental search across expanded treeEnterto edit a file, then return to the browserSpaceon a file to open a QuickLook-style preview (scroll with the mouse wheel or trackpad,↑/↓,C-d/C-u,g/G;Space/esc/qto close). Binary files and files over 512KB are not rendered.→on a file to open the context menu-to go up a directorynto create a new file in the current directory (opens your editor on an empty buffer). Forssm://, an arrow-key popup lets you pick the parameter type (String / StringList / SecureString) first, defaulting to--type.
Press → on a file to open the context menu:
s3://myapp
📁 config/
📁 static/
> 📝 index.html 2.1 KB 2026-02-19 02:08
📝 readme.md 1.3 KB 2026-02-19 02:08
..
╭──────────────────╮
│ index.html │
│ > Edit │
│ Edit metadata │
│ Download to... │
│ Copy to... │
│ Move to... │
│ Versions │
│ Delete │
╰──────────────────╯
↑/↓ navigate enter select esc/← close
| Action | Description |
|---|---|
| Edit | Open the file in your editor |
| Edit metadata | Edit ContentType, CacheControl, etc. as YAML |
| Download to... | Download to a local directory (tab completion supported) |
| Copy to... | Copy to another key in the same bucket |
| Move to... | Move to another key in the same bucket |
| Versions | View version history and restore a previous version (S3 / GCS / Azure Blob versioning) |
| Delete | Delete the object (with confirmation) |
View and restore previous versions of objects (requires versioning enabled — S3 bucket versioning, GCS object versioning, or Azure Blob versioning).
# Open version history directly
ladle --versions s3://myapp/config.json ██ _ ___ _ ____
██ /_\ | _ \| | | __|
▄▄██▄ / _ \| | || |__| _|
██████_/ \_\___/|____|____|
▀██▀ v1.0.0
s3://myapp
╭──────────────────────────────────────╮ ╭──────────────────────────────────╮
│ Versions: config.json │ │ Preview │
│ > aB3dE6fG7h1i 2026-02-27 10:30 │ │ { │
│ 812 B (current) │ │ "debug": true, │
│ kL9mN0pQ2r3s 2026-02-26 14:15 │ │ "port": 8080, │
│ 795 B │ │ "host": "0.0.0.0" │
│ tU4vW5xY6z7a 2026-02-25 09:00 │ │ } │
│ 780 B │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
╰──────────────────────────────────────╯ ╰──────────────────────────────────╯
↑/↓ navigate wheel in Preview scroll ctrl-d/u scroll preview enter restore esc back esc×2 quit
The version view shows a list of versions on the left with a content preview on the right. Use ↑/↓ to navigate versions, the mouse wheel or trackpad over the Preview pane (or Ctrl-d/Ctrl-u) to scroll it, and Enter to restore a selected version.
You can also access version history from the browser's context menu by selecting Versions on any file.
ssm:// edits AWS Systems Manager Parameter Store parameters with the same
edit → diff → confirm flow. Parameter Store has no buckets: the whole path after
the scheme is the parameter name, normalized to a single leading slash, so
ssm://myapp/db and ssm:///myapp/db both mean /myapp/db.
# Edit a parameter value in your editor
ladle ssm:///myapp/prod/db-url
# Read / write via pipes (agent- and script-friendly)
ladle ssm:///myapp/prod/db-url > value.txt # read value to stdout
echo -n 'postgres://new/db' | ladle --yes ssm:///myapp/prod/db-url
# Create a new parameter (defaults to String; use --type for others)
echo -n 's3cret' | ladle --yes --type SecureString ssm:///myapp/prod/api-token
# List a path (directories keep a trailing slash); --recursive for the whole tree
ladle ssm:///myapp/prod/
ladle ssm:///myapp/ --recursive
# Version history (tab-separated: version, mtime, type, modified-by, LATEST)
ladle --versions ssm:///myapp/prod/db-url
# Parameter attributes as YAML (type, tier, keyId, description, dataType)
ladle --meta ssm:///myapp/prod/db-password
ladle --meta ssm:///myapp/prod/db-password > meta.yamlSecureString values are never exposed by default. Any operation that would
reveal plaintext — editing, piping the value out, or diffing an update — refuses
unless you pass --reveal:
ladle --reveal ssm:///myapp/prod/db-password # decrypt and edit
ladle --reveal ssm:///myapp/prod/db-password > secret # decrypt to stdoutNotes:
- On write, the original KMS key (
keyId) and other attributes are preserved. - Editing a SecureString's metadata re-writes the parameter (SSM has no
metadata-only API), so
--metaon a SecureString also needs--reveal. - A SecureString value can still be updated non-interactively without
--revealby using--yes, which skips the (plaintext) diff (e.g.echo -n "$SECRET" | ladle --yes ssm:///myapp/prod/db-password). --appendto a SecureString always needs--reveal(it must read the current value to prepend it), even with--yes.- Piping into a new parameter creates a
Stringunless you pass--type(String|StringList|SecureString), so secrets aren't stored in cleartext by accident. - Temp files are created
0600in a private directory and removed on exit.
Required IAM actions (scopable to the parameter / path ARN):
ssm:GetParameter (read), ssm:GetParametersByPath (list & browse),
ssm:GetParameterHistory (metadata & --versions), ssm:PutParameter (write),
and ssm:DeleteParameter (browser delete / move); plus kms:Decrypt /
kms:Encrypt on the key for SecureString.
Interactive directory URIs open the same TUI browser as S3 (tree navigation,
/ filter, and a context menu for edit / metadata / versions / download /
copy / move / delete). A name that is actually a namespace (has children but is
not itself a parameter) opens the browser too, so a missing trailing slash still
works. When stdout is redirected/piped, a listing is printed instead. In the
browser, SecureString values are masked in the version-preview pane unless
--reveal is set.
brew install jingu/tap/ladlego install github.com/jingu/ladle/cmd/ladle@latestDownload the binary for your platform from Releases.
ladle --profile production s3://bucket/file.html
ladle --region ap-northeast-1 s3://bucket/file.html
ladle --endpoint-url http://localhost:9000 s3://bucket/file.html # MinIO
ladle --no-sign-request s3://public-bucket/file.htmlladle supports Google Cloud Storage via the gs:// scheme:
ladle gs://bucket/path/to/file.html
ladle gs://bucket/path/to/ # file browser mode
ladle --project myproject gs:// # list bucketsCredentials are resolved via Application Default Credentials (ADC):
GOOGLE_APPLICATION_CREDENTIALSpointing at a service account key filegcloud auth application-default login- the attached service account when running on GCP (GCE, Cloud Run, GKE, etc.)
gcloud auth application-default login
ladle gs://bucket/file.htmlListing buckets (ladle gs://) requires a project ID, set via --project or the
GOOGLE_CLOUD_PROJECT environment variable. Use --no-sign-request for public
buckets, and --endpoint-url (or STORAGE_EMULATOR_HOST) to target the
fake-gcs-server emulator.
ladle supports Azure Blob Storage via the az:// scheme, where the container
maps to the bucket and the blob maps to the key:
ladle --account myaccount az://container/path/to/file.html
ladle az://container/path/to/file.html # with AZURE_STORAGE_ACCOUNT set
ladle az:// # list containersThe storage account and credentials are resolved in this priority order:
AZURE_STORAGE_CONNECTION_STRING— full connection string- account name (
--accountorAZURE_STORAGE_ACCOUNT) +AZURE_STORAGE_KEY— shared key - account name +
AZURE_STORAGE_SAS_TOKEN— SAS token - account name + Azure AD (
DefaultAzureCredential, e.g.az loginor Managed Identity)
export AZURE_STORAGE_ACCOUNT=myaccount
export AZURE_STORAGE_KEY=... # or
export AZURE_STORAGE_CONNECTION_STRING=... # or just `az login` for Azure AD
ladle az://container/file.htmlUse --endpoint-url to target the Azurite emulator.
| Flag | Short | Description |
|---|---|---|
--meta |
Edit object metadata instead of file content | |
--versions |
Show version history for a file (S3 / GCS / Azure Blob versioning) | |
--editor |
Editor command (overrides env vars) | |
--yes |
-y |
Skip confirmation prompt |
--dry-run |
Show diff without uploading | |
--force |
Force editing of binary files | |
--reveal |
Decrypt and expose SecureString values (ssm://) |
|
--recursive |
List parameters recursively (ssm://) |
|
--type |
Type when creating a new ssm:// parameter (String|StringList|SecureString) |
|
--profile |
AWS named profile | |
--region |
AWS region | |
--account |
Azure storage account name (or AZURE_STORAGE_ACCOUNT) |
|
--project |
GCP project ID for bucket listing (or GOOGLE_CLOUD_PROJECT) |
|
--endpoint-url |
Custom endpoint URL (MinIO, LocalStack, Azurite, fake-gcs-server, etc.) | |
--no-sign-request |
Do not sign requests | |
--install-completion |
Generate shell completion script (bash|zsh|fish) |
ladle selects an editor in this order:
--editorflagLADLE_EDITORenvironment variableEDITORenvironment variableVISUALenvironment variablevi(fallback)
# bash
ladle --install-completion bash >> ~/.bashrc
# zsh
ladle --install-completion zsh >> ~/.zshrc
# fish
ladle --install-completion fish > ~/.config/fish/completions/ladle.fishladle ships an Agent Skill that teaches AI coding agents how to read, edit, and inspect cloud storage objects with ladle (using pipe mode so it works non-interactively).
# Install for Claude Code (user-global: ~/.claude/skills/ladle/SKILL.md)
ladle skill install
# Install into the current project (.claude/skills/ladle/SKILL.md)
ladle skill install --project
# Overwrite an existing install
ladle skill install --force
# Print the skill to stdout
ladle skill show- Cloudflare R2 (
r2://) backend - Multi-file batch editing
ladle comparefor diffing two remote files
MIT