Skip to content

CLI Commands ​

Complete reference for the aeroftp-cli binary. It shares the same Rust backend as the desktop app, with direct URL support for core protocols, saved-profile access for GUI-authorized providers, structured JSON output, and Unix pipeline compatibility.

Coverage: 7 transport protocols (FTP, FTPS, SFTP, WebDAV, S3, Azure Blob, OpenStack Swift) + 20+ native provider integrations + 40+ pre-configured presets. See Protocol Overview for the full integration matrix.

Connection Methods ​

URL Format ​

protocol://user:password@host:port/path

These transports + direct-auth providers support URL connections:

ProtocolURL SchemeAuth Method
FTPftp://Password
FTPSftps://Password + TLS
SFTPsftp://Password / SSH Key
WebDAVwebdav:// / webdavs://Password
S3s3://Access Key + Secret
Azure Blobazure://HMAC / SAS Token
MEGA.nzmega://Password (E2E)
Filenfilen://Password (E2E)
Internxtinternxt://Password (E2E)
Jottacloudjottacloud://Bearer Token
FileLufilelu://API Key
Koofrkoofr://OAuth2 Token
OpenDriveopendrive://Password
Yandex Diskyandexdisk://OAuth2 (via --profile)
GitHubgithub://PAT / Device Flow
Local folder/abs/path, ./rel/path, file:///abs/pathNone (a folder of this machine)

A Local Folder as the Remote ​

Every command that takes a URL also takes a folder of this machine: an absolute path, a path that starts at the current directory (., .., ./x, ../x) or a file:///abs/path URL. A bare word such as share stays a URL error, because it is more often a profile name typed without --profile.

bash
aeroftp-cli serve webdav /srv/share                 # share a folder over WebDAV
aeroftp-cli ls file:///srv/share /docs -l
aeroftp-cli put ./backup /home/me/report.pdf /reports/
aeroftp-cli sync /mnt/archive ./photos /2026 --direction upload
aeroftp-cli hashsum -a sha256 /srv/share /iso/image.iso   # computed in place, no download
  • The folder is the root of a path jail: .. stops at it and a symbolic link that resolves outside it is refused, for reads and for writes. A served client cannot reach a file outside the folder.
  • Deleting or renaming a link acts on the link, never on the file it names. Names are taken as written: report and report are two files.
  • A listing shows a link only when it points to a file inside the folder; the others are left out and named in a warning.
  • An upload keeps the modification time of its source, and an upload onto its own source is refused.
  • hashsum, check --checksum and sync --checksum get md5, sha1, sha256, sha512 or blake3 by reading the file in place. df reports the filesystem that holds the folder, cp copies inside it, and an interrupted transfer resumes from the bytes already held.
  • get, put and sync move whole files, one at a time; serve sftp and a resumed serve ftp read a served file by range.
  • sync /a /b with two local folders and no REMOTE keeps the local-to-local copier. Give a REMOTE other than / to sync against the first folder as a remote with the full sync engine.

Available from v4.2.3.

Saved profiles are the preferred path for browser-authorized and profile-backed API providers such as Google Drive, Dropbox, OneDrive, Box, pCloud, Zoho WorkDrive, Yandex Disk, 4shared, and Drime. 4shared (OAuth 1.0) tokens are automatically loaded from the vault after GUI authorization.

Server Profiles (--profile) ​

Connect to any saved server from the encrypted vault with zero credentials exposed in shell history or process lists.

bash
# List all saved profiles
aeroftp-cli profiles

# Connect by name (fuzzy substring matching)
aeroftp-cli ls --profile "My Server" /path/

# Connect by index number
aeroftp-cli ls --profile 3 /

Profile matching order: exact name (case-insensitive), exact ID (UUID), substring match (auto-selects if unique, lists candidates if ambiguous).

Password Handling ​

In order of preference:

  1. stdin (most secure): echo "$PASS" | aeroftp-cli --password-stdin connect sftp://user@host
  2. Environment variable: AEROFTP_TOKEN=mytoken aeroftp-cli connect jottacloud://user@host
  3. Interactive prompt: Hidden TTY input when no password provided
  4. URL (least secure): sftp://user:password@host - warning always displayed

Master password for vault: set AEROFTP_MASTER_PASSWORD env var or enter interactively.

Commands ​

connect ​

Test connectivity, display server info, and disconnect.

bash
aeroftp-cli connect sftp://user@host
aeroftp-cli connect sftp://user@host --key ~/.ssh/id_ed25519
aeroftp-cli connect ftp://user@host --tls explicit --insecure

ls ​

bash
aeroftp-cli ls sftp://user@host /var/www/ -l          # Long format
aeroftp-cli ls sftp://user@host / --sort size --reverse
aeroftp-cli ls --profile "NAS" / --all --json

# v3.6.6: agent-friendly cap + filters
aeroftp-cli ls --profile "NAS" /huge-dir --limit 200 --json
aeroftp-cli ls --profile "NAS" /backups --files-only      # only regular files
aeroftp-cli ls --profile "NAS" /var/www --dirs-only       # only directories

The summary in --json carries truncated: bool and total_before_limit: int so agents can detect partial results unambiguously when --limit was used. The same trio (--limit, --files-only, --dirs-only) is available on find, plus a --name <glob> alias for the positional pattern (added v3.6.6).

get / put ​

bash
# Download with glob pattern
aeroftp-cli get sftp://user@host "/data/*.csv"

# Recursive download
aeroftp-cli get sftp://user@host /var/www/ ./backup/ -r

# Upload with glob
aeroftp-cli put sftp://user@host "./*.json" /data/

# Recursive upload
aeroftp-cli put sftp://user@host ./dist/ /var/www/dist/ -r

pget - Segmented Parallel Download (v3.6.5) ​

bash
# Default: 4 parallel segments
aeroftp-cli pget --profile "AWS S3" /backup/big.tar.gz ./big.tar.gz

# Tune segment count (range 2-16)
aeroftp-cli pget --profile "AWS S3" /backup/big.tar.gz --segments 8

# JSON progress output
aeroftp-cli pget --profile "AWS S3" /backup/big.tar.gz --json

pget is now a real subcommand (alias of get with the --segments 4 preset). Splits a single file into N byte ranges and downloads them concurrently, then stitches them back. Useful when latency or per-connection throughput is the bottleneck (large .tar.gz archives, S3 buckets in far regions). Falls back to a single sequential stream when the provider does not advertise range-request support.

One download per destination file at a time. A segmented download writes into <name>.aerosegtmp beside the destination and renames it into place once every segment is in. On Linux, on a local filesystem, that temporary is locked for the whole run, so a second segmented download of the same file into the same path is refused while the first one runs. Everywhere else the lock is not taken, because there it could block other programs or the download itself: on macOS, on Linux network and FUSE mounts (NFS, SMB, sshfs) and on other Unix systems, a second download discards the first one's temporary, and the first can then finish with an incomplete file. On Windows the second download fails instead, because the first still has the temporary open. Locks are per machine on every system: two machines writing the same path on a shared mount never see each other. The same holds for segmented downloads started from the app.

mkdir / rm / mv ​

bash
aeroftp-cli mkdir sftp://user@host /var/www/new-folder
aeroftp-cli mkdir --profile "S3" /bucket/new -p          # idempotent create-parents (v3.5.2)
aeroftp-cli mkdir --profile "S3" /bucket/new -p --json   # already_existed: bool reported (v3.6.6)

aeroftp-cli rm sftp://user@host /tmp/old-dir/ -rf
aeroftp-cli rm --profile "server" /missing-file --force  # idempotent delete (suppresses NotFound)

aeroftp-cli mv sftp://user@host /docs/draft.md /docs/final.md

Since v4.2.1, rm without -r never erases a folder that still holds files: on every backend a non-recursive delete lists first and refuses a non-empty folder (exit 9), and a listing that fails removes nothing. rm --dry-run on a non-empty directory exits 9 like the real run.

cat / stat / find / df / tree ​

bash
aeroftp-cli cat sftp://user@host /etc/config.ini | grep DB_HOST
aeroftp-cli stat sftp://user@host /var/www/index.html --json
aeroftp-cli find sftp://user@host /var/www/ "*.php"
aeroftp-cli df sftp://user@host
aeroftp-cli tree sftp://user@host /var/www/ -d 2

head / tail ​

bash
# First 5 lines of a remote file
aeroftp-cli head --profile "server" /var/log/app.log -n 5

# Last 20 lines (default)
aeroftp-cli tail --profile "server" /var/log/app.log

# JSON output
aeroftp-cli head --profile "server" /file.txt -n 3 --json

# v3.6.6: byte-range preview (works on binary content too)
aeroftp-cli head --profile "S3" /backups/db.dump -c 4096 --json
aeroftp-cli head --profile "S3" /backups/db.dump --bytes 8192 --json

The -c / --bytes <N> flag (added v3.6.6) returns the first N bytes of any remote file without downloading the whole thing. JSON output reports bytes_returned, total_size, truncated, and encoding (utf-8 for text, base64 when bytes are not valid UTF-8). Closes the "no way to preview the first 4 KiB without a full download" gap surfaced by the AI-agent black-box audit.

touch ​

bash
# Create empty file
aeroftp-cli touch --profile "server" /remote/newfile.txt

# Verify existing file (no error)
aeroftp-cli touch --profile "server" /remote/existing.txt

hashsum ​

Algorithms: md5, sha1, sha256, sha512, blake3, chosen with -a / --algorithm (default sha256).

bash
aeroftp-cli hashsum --profile "server" -a sha256 /data/file.bin
aeroftp-cli hashsum sftp://user@host -a blake3 /path/file.dat --json

Output matches standard sha256sum format: <hash> <path>.

check ​

Verify that a local directory matches a remote directory. The rclone check-equivalent for AeroFTP: scans both sides in parallel, classifies every file, and reports differences without modifying anything.

bash
# Compare by size + mtime (default, fast)
aeroftp-cli check --profile "server" /local/dir /remote/dir

# Compare by SHA-256 checksum (downloads the remote bytes for hashing)
aeroftp-cli check --profile "server" /local/ /remote/ --checksum

# One-way: only check files present locally, ignore extra files on the remote
aeroftp-cli check --profile "server" /local/ /remote/ --one-way

# Machine-readable output for CI / scripting
aeroftp-cli check --profile "server" /local/ /remote/ --json

Output classes ​

Each entry is classified into exactly one of:

StatusMeaning
matchFile present on both sides with same size (and same checksum if --checksum)
differFile present on both sides but differs (size or checksum mismatch)
missing_localFile exists only on the remote (suppressed by --one-way)
missing_remoteFile exists only locally

JSON envelope ​

json
{
  "status": "ok" | "differences_found" | "partial",
  "match_count": 1234,
  "differ_count": 0,
  "missing_local": 5,
  "missing_remote": 2,
  "elapsed_secs": 4.21,
  "details": [
    {"path": "docs/api.md", "status": "differ", "local_size": 4096, "remote_size": 4123},
    {"path": "build/output.bin", "status": "missing_remote", "local_size": 81920, "remote_size": null}
  ],
  "suggested_next_command": "aeroftp-cli sync --profile \"server\" \"/local/\" \"/remote/\" --dry-run --json"
}

The suggested_next_command field is the natural follow-up: a sync --dry-run you can review before applying changes.

Exit codes ​

CodeMeaning
0OK: no differences
4Differences found, or the result is partial (a scan could not read its whole tree or left out a path it can name, listed in local_scan_boundaries / remote_scan_boundaries), or the run was refused because a scan has a gap it cannot name
5Local path is not a directory
6Connection / authentication failed
2Remote path not found

The 4 on diff makes check directly usable as a CI gate:

bash
# Block deploy if remote drifted from the source tree
aeroftp-cli check --profile "prod-cdn" ./public /assets --checksum && ./deploy.sh

about ​

Detailed server info with storage quota - more comprehensive than df.

bash
# Server info with storage quota
aeroftp-cli about --profile "server"

# JSON output
aeroftp-cli about --profile "server" --json

Shows provider name, type, server software, protocol version, connection parameters, and storage quota (used/free/total) when available. Some object-storage providers do not expose quota via the upstream API, so about and df may return provider info without quota fields.

dedupe ​

Find and remove duplicate files by content hash (SHA-256).

bash
# Scan for duplicates (report only)
aeroftp-cli dedupe --profile "server" /data --dry-run

# Delete duplicates (keep first occurrence)
aeroftp-cli dedupe --profile "server" /data --mode skip

# JSON output with group details
aeroftp-cli dedupe --profile "server" /data --dry-run --json

Groups files by size first (fast pre-filter), then hashes to confirm. Modes: skip (report only), newest, oldest, largest, smallest, rename (rename with numeric suffix), interactive (prompt per group, TTY-only), list (list without action).

cleanup ​

Scan for orphaned .aerotmp files from interrupted downloads.

bash
# Dry-run: list orphaned temp files
aeroftp-cli cleanup --profile "server" /remote/path/ --json

# Delete orphaned temp files
aeroftp-cli cleanup --profile "server" /remote/path/ --force

Dry-run by default. JSON output includes orphans, bytes, and bytes_freed.

alias-toggle ​

Enable or disable the optional aero short alias. The same command toggles it on and off, and is idempotent (exit code 0 in both directions).

bash
# Toggle the 'aero' alias on/off
aeroftp-cli alias-toggle aero

# Override the target directory (default: ~/.local/bin)
aeroftp-cli alias-toggle aero --bin-dir ~/bin

# JSON output for scripting
aeroftp-cli --json alias-toggle aero

The aftp short name is built in and always present, so it needs no toggle. See Shorter Command Names for the full explanation. Added at the community's request (discussion #273).

sync ​

bash
# Preview changes
aeroftp-cli sync --profile "server" ./local/ /remote/ --dry-run

# Upload only
aeroftp-cli sync --profile "server" ./local/ /remote/ --direction upload

# Mirror mode (delete orphans)
aeroftp-cli sync sftp://user@host ./local/ /remote/ --delete

# Exclude patterns
aeroftp-cli sync --profile "server" ./local/ /remote/ --exclude "*.tmp" --exclude ".git"

# Safety: abort if too many deletes
aeroftp-cli sync --profile "server" ./local/ /remote/ --delete --max-delete 50
aeroftp-cli sync --profile "server" ./local/ /remote/ --delete --max-delete 25%

# Detect renamed files to avoid re-upload
aeroftp-cli sync --profile "server" ./local/ /remote/ --delete --track-renames --dry-run
# Without --track-renames: 1 upload + 1 delete
# With --track-renames: 1 server-side rename (no data transfer)

# Time-based bandwidth schedule
aeroftp-cli sync --profile "server" ./local/ /remote/ --bwlimit "08:00,512k 12:00,10M 18:00,512k 23:00,off"

# Simple bandwidth limit
aeroftp-cli sync --profile "server" ./local/ /remote/ --bwlimit "1M"

# Bisync with conflict resolution
aeroftp-cli sync --profile "server" ./local/ /remote/ --conflict-mode newer
aeroftp-cli sync --profile "server" ./local/ /remote/ --conflict-mode skip --dry-run

# Rename mode: keep both versions (local uploaded as .conflict-{timestamp})
aeroftp-cli sync --profile "server" ./local/ /remote/ --conflict-mode rename

# Immutable mode: never overwrite existing files
aeroftp-cli sync --profile "server" ./local/ /remote/ --immutable

# Transfer only listed files
aeroftp-cli sync --profile "server" ./local/ /remote/ --files-from list.txt

# Skip remote listing (assume empty destination)
aeroftp-cli sync --profile "server" ./local/ /remote/ --no-check-dest

# S3 optimization: recursive listing in a single API call
aeroftp-cli sync --profile "S3" ./local/ /remote/ --fast-list

# Force full resync (discard snapshot)
aeroftp-cli sync --profile "server" ./local/ /remote/ --resync

# Backup before overwrite/delete
aeroftp-cli sync --profile "server" ./local/ /remote/ --delete --backup-dir /tmp/bak

# One-way update (v4.2.1): leave a destination copy that is newer than the source
aeroftp-cli sync --profile "server" ./local/ /remote/ --direction upload --update

# One-way conflict handling (v4.2.1): skip a changed pair the dates cannot order
aeroftp-cli sync --profile "server" ./local/ /remote/ --direction upload --conflict-mode skip

# Compare same-size files by server-side checksum instead of time (v4.2.1)
aeroftp-cli sync --profile "server" ./local/ /remote/ --checksum --dry-run

# Modification window (v4.2.1): seconds within which two times are the same instant
aeroftp-cli sync --profile "server" ./local/ /remote/ --direction download --modify-window 60

Bisync (--direction both, the default) saves a .aeroftp-bisync.json snapshot after each successful sync, enabling delta detection and bidirectional delete propagation. Conflict modes: newer (default), older, larger, smaller, skip, rename.

One-way sync (--direction upload or download, v4.2.1) has its own rules:

  • Two times within 2 s - or --modify-window SECS - are the same instant; the window is raised to the precision the backend keeps, and a backend with no comparable time is compared by size only, which the run says on stderr and in the JSON result.
  • --update applies to a changed pair only: it leaves one alone when the destination copy is newer than the source, as rsync's --update does. It is refused with --direction both.
  • A changed pair the dates cannot order is a conflict: --conflict-mode source (the one-way default) transfers it, --conflict-mode skip leaves it. Every pair left open is named on stderr and listed in the JSON result under conflicts_open.
  • --checksum compares same-size files by content (the backend's server-side checksum against a local digest, strongest algorithm first); it cannot be combined with --size-only / --skip-matching.

Other v4.2.1 corrections:

  • A percentage --max-delete N% caps each side on its own at N percent of the files the scan found there, rounded down: --max-delete 50% allows 1 delete of 3 files and none of 1, so a cap under 100% never empties a side.
  • Under --immutable, a planned transfer whose destination exists with a different size is refused as an error (exit 4, named in the JSON errors) - it is most often the partial an interrupted upload left - and one of the same size is counted in skipped.
  • With --delete, a one-way sync also removes the destination directories its deletes left empty, deepest first, on every backend. The dry run lists them as RMDIR (..., if empty after the deletes); the JSON result counts the removed ones in dirs_deleted and lists every folder kept after a refused removal, with the reason, under dirs_kept.
  • --exclude (repeatable, joined by the global --exclude-global and --exclude-from) is read by sync, sync --watch, sync-doctor and reconcile with the matcher the desktop app uses, case-insensitively. A bare name such as node_modules or .git matches any folder or file of that name, and an excluded folder takes everything under it with it; a pattern with / matches that run of folders anywhere in the tree, and a leading / anchors it at the sync root. An invalid pattern, or an --exclude-from file that cannot be read, is a usage error (exit 5): before v4.2.1 an invalid pattern was dropped silently. Patterns may now match more than before, so review exclude lists written under the old, narrower reading.
  • Ctrl+C stops a sync in any phase and ends it with exit 130 and "status": "interrupted"; get -r, put -r and get / put with a glob do the same. A second Ctrl+C force-exits.

reconcile ​

Categorized local-vs-remote diff. Returns 4 buckets (matches / differs / missing-remote / missing-local) for AI agents and CI pipelines that need to plan a sync before executing it.

bash
# Detailed diff with all 4 categories
aeroftp-cli reconcile --profile "server" ./local /remote --json > diff.json

# Summary mode (counts only)
aeroftp-cli reconcile --profile "server" ./local /remote --reconcile-format summary

# Feed the diff into a sync without rescanning
aeroftp-cli sync --profile "server" ./local /remote --from-reconcile diff.json --direction upload

sync-doctor ​

Pre-sync preflight checks. Emits a JSON report with planned upload/download/delete counts, bandwidth estimate, top-level diff buckets, and a next_command field with the exact aeroftp-cli sync ... invocation that matches the preflight.

bash
aeroftp-cli sync-doctor --profile "server" ./local /remote --json

Recommended discovery surface for AI coding agents before they execute any mutating sync.

speed ​

Bandwidth benchmark - uploads a synthetic file, downloads it back, reports upload/download MB/s, latency, and round-trip time. v3.6.6 hardened the path: random non-compressible payload (was zeros), SHA-256 integrity check, TTFB measurement, optional --no-integrity flag, automatic password redaction in every output stream.

bash
# Default (10 MB synthetic file)
aeroftp-cli speed --profile "server"

# Custom size (alias --size / -s)
aeroftp-cli speed --profile "server" --size 100M

# Skip integrity SHA-256 (faster on slow CPUs)
aeroftp-cli speed --profile "server" --size 100M --no-integrity

# JSON output (schema: aeroftp.speedtest.v1)
aeroftp-cli speed --profile "server" --size 50M --json

speed-compare (v3.6.6) ​

Multi-server benchmark in parallel. Emits a ranked comparison table with download/upload throughput, TTFB, and SHA-256 integrity status.

bash
# Rank two or more servers side-by-side
aeroftp-cli speed-compare sftp://user@host1 sftp://user@host2 sftp://user@host3

# Custom test size and parallelism (up to 4 parallel runs, default 2)
aeroftp-cli speed-compare --size 100M --parallel 4 sftp://user@host1 sftp://user@host2

# Persist reports to disk (JSON v1 + CSV + Markdown)
aeroftp-cli speed-compare sftp://user@host1 sftp://user@host2 \
    --json-out report.json --csv-out report.csv --md-out report.md

Schema aeroftp.speedtest.v1 is stable across single-server (speed) and multi-server (speed-compare) reports. The CLI matches the GUI's Server SpeedTest dialog (Single / Compare tabs, multi-select up to 8 servers, regression warning when last run is < median × 0.7).

Output safety: redact_url_for_display() strips passwords from every output path (stdout / JSON / CSV / MD); csv_cell_safe() neutralizes spreadsheet formula triggers (=, +, -, @); md_cell_safe() escapes pipes, newlines and backslashes. SQLite history (WAL mode, 0600/0700 perms, 1000-row cap) keeps a local benchmark log.

transfer ​

Cross-profile copy between two saved servers.

bash
# Preview plan and risk summary
aeroftp-cli transfer-doctor "FTP Aruba" "AWS S3" /www.site.it /backup/site --json

# Execute recursive copy
aeroftp-cli transfer "FTP Aruba" "AWS S3" /www.site.it /backup/site --recursive

# Skip existing files on destination
aeroftp-cli transfer "Cloudflare R2" "Wasabi" /logs /archive/logs --recursive --skip-existing

Use this when both endpoints are already stored as AeroFTP profiles. transfer-doctor is the recommended first step for automation because it returns the plan, risks, and suggested next command.

mount ​

Mount any remote as a local FUSE filesystem. Any application can access remote files with standard tools.

The GUI AeroMount (v3.7.1) wraps this command with a persistent registry: save mount configs (profile, remote path, mountpoint, read-only, cache TTL, allow-other, autostart) once and mount them with a single click. Autostart installs systemd-user units on Linux and Task Scheduler ONLOGON entries on Windows. The registry can live in a daemon-friendly sidecar JSON or in the encrypted vault.

bash
# Mount S3 as local directory
mkdir /mnt/cloud
aeroftp-cli --profile "S3 Storj" mount /mnt/cloud

# Read-only mode
aeroftp-cli --profile "server" mount /mnt/remote --read-only

# Custom cache TTL
aeroftp-cli --profile "NAS" mount /mnt/nas --cache-ttl 60

# Windows: mount as drive letter
aeroftp-cli --profile "server" mount Z:

Full read-write: ls, cat, cp, vim, grep, mkdir, rm, touch, mv, df. File managers (Nautilus, Dolphin, Explorer) browse the mount natively. Linux/macOS use FUSE, Windows uses a WebDAV bridge mapped as network drive. Unmount: fusermount -u /mnt/cloud or Ctrl+C.

ncdu ​

Interactive TUI disk usage explorer for remote storage.

bash
aeroftp-cli --profile "server" ncdu /          # Interactive TUI
aeroftp-cli --profile "server" ncdu / -d 5     # Depth limit
aeroftp-cli --profile "server" ncdu / --export /tmp/usage.json   # Export JSON
aeroftp-cli --profile "server" ncdu / --json   # JSON to stdout

TUI controls: Up/Down navigate, Enter opens directory, Backspace goes back, q quits. Directories sorted by size descending with visual percentage bars.

serve ​

Expose any remote, or a folder of this machine, as a local server of the chosen protocol. Ctrl+C to stop.

bash
# HTTP (read-only, range requests, directory listing)
aeroftp-cli --profile "server" serve http _ / --addr 127.0.0.1:8080

# WebDAV (read-write, 8 methods)
aeroftp-cli --profile "server" serve webdav _ / --addr 127.0.0.1:8080

# FTP (read-write, passive mode)
aeroftp-cli --profile "server" serve ftp _ / --addr 0.0.0.0:2121 --allow-remote-bind --passive-ports 49152-49200

# SFTP (read-write, ED25519 host key)
aeroftp-cli --profile "server" serve sftp _ / --addr 0.0.0.0:2222 --allow-remote-bind

# A folder of this machine instead of a remote
aeroftp-cli serve webdav /srv/share

Every mode listens on loopback by default; a non-loopback --addr is refused unless --allow-remote-bind is given. On a loopback address no login is asked unless you set one. On any other address a login is always required: HTTP and WebDAV take --auth-token, generated and printed at start when it is not given; FTP and SFTP take --auth-user and --auth-password, where a missing user is aeroftp and a missing password is generated and printed at start. With a local folder, the folder is the jail root and nothing outside it can be reached.

Any FTP/SFTP/WebDAV/HTTP client can now access AeroFTP's 7 transport protocols and 20+ native provider integrations (plus the GitHub repository backend) as if they were standard local servers.

daemon ​

Background service for persistent mounts, scheduled transfers, and job management.

bash
aeroftp-cli daemon start                  # Start (HTTP API on port 14320)
aeroftp-cli daemon status                 # Check status + job counts
aeroftp-cli daemon stop                   # Stop
curl http://localhost:14320/health         # Health check

jobs ​

Manage background transfer jobs (requires daemon running).

bash
aeroftp-cli jobs add get --profile "S3" /backups/db.sql ./   # Queue a job
aeroftp-cli jobs list                                        # List all jobs
aeroftp-cli jobs status <id>                                 # Check one job
aeroftp-cli jobs cancel <id>                                 # Cancel

Jobs are persisted in SQLite (~/.config/aeroftp/jobs.db) and survive daemon restarts.

crypt ​

Zero-knowledge encrypted storage on any provider: the CLI surface of the AeroCrypt overlay. Content encrypted with AES-256-GCM-SIV (64 KB blocks) under a per-file random key wrapped with AES-256-KW, filenames encrypted with AES-256-SIV, key derivation via Argon2id (128 MiB). New objects are written as the hardened v3 (AECR) format; legacy v1/v2 overlays stay readable.

bash
# Initialize encrypted overlay
AEROFTP_CRYPT_PASSWORD=MySecret aeroftp-cli --profile "S3" crypt init _ /encrypted

# Upload (content + filename encrypted)
AEROFTP_CRYPT_PASSWORD=MySecret aeroftp-cli --profile "S3" crypt put ./secret.pdf _ /encrypted

# List (decrypted names visible)
AEROFTP_CRYPT_PASSWORD=MySecret aeroftp-cli --profile "S3" crypt ls _ /encrypted

# Download + decrypt
AEROFTP_CRYPT_PASSWORD=MySecret aeroftp-cli --profile "S3" crypt get secret.pdf _ /encrypted ./decrypted.pdf

The cloud provider never sees file names or content. Password via env: AEROFTP_CRYPT_PASSWORD.

batch ​

Execute .aeroftp script files with 17 commands, shell-like variable substitution, and error policies.

bash
aeroftp-cli batch deploy.aeroftp
bash
# deploy.aeroftp
SET SERVER=sftp://deploy@prod.example.com:2222
ON_ERROR FAIL

CONNECT ${SERVER}
PUT ./dist/app.js /var/www/app.js
PUT ./dist/index.html /var/www/index.html
STAT /var/www/index.html
ECHO Deployment complete
DISCONNECT

Batch commands: SET, ECHO, ON_ERROR, CONNECT, DISCONNECT, LS, GET, PUT, MKDIR, RM, MV, CAT, STAT, FIND, DF, TREE, SYNC. Variables use ${VAR} syntax with single-pass expansion (injection-safe). Error policies: ON_ERROR FAIL (default), ON_ERROR CONTINUE.

agent ​

Run AeroAgent from the CLI for natural-language workflows, vault-backed server access, or MCP integration.

bash
# One-shot instruction
aeroftp-cli agent -m "List files on the Production server" --json

# Explicit provider/model
aeroftp-cli agent --provider xai --model grok-3-mini \
  -m "Check disk usage on all saved servers" -y --json

# Native MCP server over stdio
aeroftp-cli agent --mcp

Current agent integrations:

  • aeroftp-cli agent -m ...: one-shot orchestration through AeroAgent with approval controls
  • aeroftp-cli agent --mcp / aeroftp-cli mcp: native Model Context Protocol server for Claude Desktop, Cursor, VS Code, and other MCP clients

The MCP server exposes 39 curated remote tools (see MCP Overview), resource discovery for saved profiles/capabilities/connections, reusable prompts, connection pooling with auto-reset, per-profile request serialization, request cancellation, rate limiting, schema validation, and audit logging.

mcp (top-level shortcut) ​

bash
# Start the MCP server on stdin/stdout
aeroftp-cli mcp

aeroftp-cli mcp is a top-level alias for aeroftp-cli agent --mcp (added in v3.5.4). The official VS Code extension axpdev-lab.aeroftp-mcp registers exactly this argv, so the shortcut keeps the extension self-contained without nested subcommands. The server initializes the Universal Vault automatically (or falls back to AEROFTP_MASTER_PASSWORD when set), serializes per-profile tool calls, and validates inputSchema.required before dispatch.

Example Claude Desktop / Claude Code / Cursor configuration:

json
{
  "mcpServers": {
    "aeroftp": {
      "command": "aeroftp-cli",
      "args": ["mcp"]
    }
  }
}

Agent Discovery & Inventory (v3.6.6) ​

A dedicated set of subcommands designed for AI coding agents. They return structured JSON describing AeroFTP's capabilities, your saved profile inventory, and per-profile authentication state - all without attempting any live connection - so an agent can plan its workflow in one round-trip instead of probing N profiles.

profiles ​

bash
aeroftp-cli profiles            # text table mirroring the GUI My Servers layout
aeroftp-cli profiles --json     # canonical input for agents
aeroftp-cli profiles --csv      # spreadsheet-friendly export
aeroftp-cli profiles -i         # interactive prompt loop (v3.7.1)

Lists every server profile in the encrypted vault. The text format mirrors the GUI My Servers table (storage Used / Total / %, per-protocol footer, dedup-aware totals); --json and --csv carry the same columns plus per-profile auth_state (valid / expired / needs_refresh / no_credentials / unknown) so agents can avoid connect-then-fail loops on profiles whose OAuth tokens expired silently. Passwords are never printed.

Interactive mode (-i, v3.7.1) ​

After the table renders, -i drops into a compact prompt loop with two-character tokens:

TokenAction
1l / l1List the contents of profile #1
2t / t2Tree of profile #2
3d / d3Delete profile #3 (gated by name-typed confirmation)
qQuit

Both orderings (number-then-letter and letter-then-number) are accepted. The loop reuses the running binary via current_exe() so the output is byte-for-byte identical to the standalone subcommand the same row would invoke.

pwd ​

Print the base path every relative path resolves against. It reads the saved profile and opens no connection, so it answers while the server is down.

bash
aeroftp-cli pwd "My NAS"          # path on stdout, context on stderr
aeroftp-cli pwd "My NAS" --json   # profile, id, protocol, host, base, resolved_root

ai-models ​

bash
aeroftp-cli ai-models            # configured AI providers + models
aeroftp-cli ai-models --json

Lists every AI provider configured in the encrypted vault with its associated models. API keys are never printed. Useful for verifying which providers are wired up before invoking agent --provider <id>.

agent-bootstrap ​

bash
# General intro payload (recommended starting call for any agent)
aeroftp-cli agent-bootstrap --json

# Task-tailored variants
aeroftp-cli agent-bootstrap --task explore --path /var/www --json
aeroftp-cli agent-bootstrap --task verify-file --remote-path /a.txt --local-path ./a.txt --json
aeroftp-cli agent-bootstrap --task transfer \
    --source-profile "FTP Aruba" --dest-profile "AWS S3" \
    --source-path /www --dest-path /backup/www --json

Returns the canonical task-oriented quick-start that agents should follow before issuing real commands. Tasks: explore, verify-file, transfer, backup, reconcile. The payload includes the recommended workflow, the saved-profile inventory with auth_state, and ready-to-run command templates.

agent-connect ​

bash
# Single JSON envelope: connect + capabilities + quota + root listing
aeroftp-cli agent-connect "AWS S3" --json

Replaces the connect &rarr; about &rarr; df &rarr; ls / boilerplate sequence agents would otherwise have to issue. Returns a JSON envelope with four blocks: connect, capabilities, quota, path. Each block carries one of ok / unsupported / unavailable / error.

Live-connect allowlist: FTP, FTPS, SFTP, WebDAV, S3, GitHub, GitLab. For protocols outside the allowlist (pCloud, Dropbox, OneDrive, Box, Filen, MEGA, Koofr, kDrive, Jottacloud, Drime, FileLu, Yandex, 4shared, Internxt, Swift, Azure, Google Drive, Zoho WorkDrive, Immich) the connect block returns status: "unsupported" but capabilities, path, and profile stay actionable, and the CLI exits 0 because the rest of the payload is still useful.

Exit codes: 0 ok or unsupported with valid capabilities, 1 connect attempted and failed, 2 profile lookup failed.

agent-info ​

bash
aeroftp-cli agent-info --json

Prints structured JSON describing safe / modify / destructive command groups, credential model, output hygiene, the saved profile inventory with per-profile auth_state, and (added v3.6.6) a protocol_features map keyed by protocol with feature tokens (share_links, resume, server_copy, versions, thumbnails, change_tracking, etc.) plus an agent_connect_supported_protocols array.

The protocol_features map collapses what used to require N agent-connect calls (one per profile) into a single batch query. Recommended discovery surface for AI coding agents before any capability-aware tool routing.


import rclone ​

Import server profiles from rclone configuration files. Supports 23 rclone backend types with automatic password de-obfuscation.

bash
# Auto-detect rclone.conf location
aeroftp-cli import rclone

# Specify path manually
aeroftp-cli import rclone /path/to/rclone.conf

# JSON output for scripting
aeroftp-cli import rclone --json

Supported rclone types: ftp, sftp, s3 (all providers), webdav (Nextcloud, ownCloud), drive, dropbox, onedrive, mega, internxt, filen, box, pcloud, azureblob, swift, yandexdisk, zohoworkdrive, koofr, jottacloud, b2, opendrive, drime, cloudinary, imagekit. Passwords are revealed from rclone's reversible AES-256-CTR obfuscation. Use the GUI import flow (Settings > Export/Import > Import from rclone) to store credentials in the encrypted vault.

Re-import profiles imported before v4.2.1

Before v4.2.1, keys rclone writes in plain text (S3, Azure Blob, Swift and B2 keys, and the Drime, Cloudinary and ImageKit secrets) went through the reveal codec on import, and a small share was stored corrupted (0.4% of B2 application keys, 2.2% of Cloudinary secrets). Since v4.2.1 they are imported exactly as written. A profile imported from rclone that fails to sign in should be imported again.

For compatibility details about existing rclone crypt remotes, see rclone crypt interoperability.

import rclone-filter ​

Convert an rclone --filter-from file into an .aeroignore file (gitignore syntax). Useful when porting an existing rclone workflow to AeroFTP sync / copy operations that read .aeroignore for path filtering.

bash
# Print the converted .aeroignore to stdout
aeroftp-cli import rclone-filter ~/.config/rclone/filter.txt

# Write directly to a file (refuses to overwrite without --force)
aeroftp-cli import rclone-filter filter.txt -o /project/.aeroignore

# Read filter rules from stdin (e.g. piping from another tool)
cat filter.txt | aeroftp-cli import rclone-filter -

# JSON envelope (status, input path, generated text, warnings)
aeroftp-cli import rclone-filter filter.txt --json

Why a converter is needed ​

rclone and gitignore look superficially similar (both use +/- style include/exclude or pattern/!pattern, both honor * and **, both ignore # comments) but the match semantics are opposite:

Aspectrclonegitignore (.aeroignore)
Match resolutionFirst matching rule winsLast matching rule wins
Include syntax+ pattern!pattern
Exclude syntax- patternpattern
Reset directive! clears all rules above(no equivalent)
Comments# and ;# only

To preserve precedence, the converter reverses the rule order: a rule that appeared first in the rclone file (and therefore had highest priority) ends up last in the .aeroignore, where it again wins under last-match-wins semantics.

Worked example ​

Input (rclone filter):

- target/
- node_modules/
- *.tmp
- *.log
+ /docs/IMPORTANT.md
+ /README.md

Output (.aeroignore):

# Generated by AeroFTP from rclone filter file
# Original rules were in first-match-wins order; here they are reversed
# to preserve semantics under gitignore last-match-wins rules.

!/README.md
!/docs/IMPORTANT.md
*.log
*.tmp
node_modules/
target/

The two + rules become ! re-includes and end up at the top, where they win against any later exclude pattern under last-match-wins.

Warnings ​

Two non-fatal warnings can be surfaced (text on stderr, JSON in the warnings array). Neither aborts the conversion.

  • ! reset directive: gitignore has no exact equivalent. The rules above the reset are kept (rather than silently discarded as rclone would do at runtime) and the warning prompts you to review and prune them manually if needed.
  • {a,b} brace alternation: gitignore does not support brace expansion. The pattern is passed through unchanged; expand manually (*.bak and *.old as separate rules) if the rule needs to apply.

JSON envelope ​

json
{
  "status": "ok",
  "input": "/path/to/filter.txt",
  "aeroignore": "# Generated by AeroFTP ...\n!/README.md\n...",
  "warnings": [
    "line 12: pattern '*.{bak,old}' uses brace alternation '{a,b}' which gitignore does not support; passed through unchanged"
  ]
}

When -o <path> is provided the envelope replaces the aeroignore field with output (the destination path) and bytes_written (the number of bytes flushed to disk).

Exit codes ​

CodeMeaning
0OK: conversion successful
2Input file not found
9Output file already exists and --force was not given
11I/O error (failed to read stdin, write the output, etc.)

The exit-code-9 design follows the convention used by aeroftp-cli put -n (--no-clobber): when refusing to overwrite, the operation is treated as "skipped" rather than a hard failure, keeping scripts that rerun the same conversion idempotent.

GitHub Protocol ​

Every upload and delete creates a real Git commit. Branch-aware with automatic working branch creation for protected branches.

bash
aeroftp-cli ls github://token:PAT@owner/repo@develop /src/ -l
aeroftp-cli put github://token:PAT@owner/repo ./fix.py /src/fix.py
aeroftp-cli cat github://token:PAT@owner/repo /README.md

Global Flags ​

FlagDescription
--profile <name> / -PUse saved server profile from encrypted vault
--master-password <pw>Vault master password (env: AEROFTP_MASTER_PASSWORD)
--json / --format jsonStructured JSON output to stdout
--quiet / -qSuppress info messages (errors only)
--verbose / -vDebug output (-vv for trace)
--password-stdinRead password from stdin pipe
--key <path>SSH private key file
--token <token>Bearer/API token (env: AEROFTP_TOKEN)
--tls <mode>FTP TLS: none, explicit, implicit, explicit_if_available
--insecureSkip TLS certificate verification
--trust-host-keyTrust unknown SSH host keys
--two-factor <code>2FA code for Filen/Internxt (env: AEROFTP_2FA)
--limit-rate <speed>Speed limit (e.g., 1M, 500K)
--bwlimit <schedule>Bandwidth schedule (e.g., "08:00,512k 18:00,off" or "1M")
--parallel <n>Parallel transfer workers for recursive and bulk operations (default 4, up to 32). Each provider has its own ceiling (SFTP 16 connections, FTP 5, S3 and B2 their clone pools, single-session providers 1); when the ceiling is lower than the request the CLI prints a note in text mode
--checkers <n>Directories listed at once by the remote scan of sync, reconcile, check and cryptcheck (default 8, range 1-64), capped by the provider's list pool
--multi-thread-streams <n>Concurrent range streams for one large download (default 4, env AEROFTP_MULTI_THREAD_STREAMS); 1 restores a single stream. Used by S3, Azure, SFTP (independent connections), and by WebDAV and Koofr after a strict 206 probe
--multi-thread-cutoff <size>Minimum file size for a multi-stream download (default 250M, env AEROFTP_MULTI_THREAD_CUTOFF)
--sftp-readahead <N>SFTP read-ahead window (2 to 1024). Default 32 when unset; AEROFTP_SFTP_READAHEAD=off (or 0) restores one read per round trip; a bandwidth cap always uses the serial loop
--skip-restrictedRecursive put: skip files or folders whose name the destination forbids, upload the rest and report each skip; the run ends partial (exit 4). By default the whole batch is refused before anything is uploaded
--partialResume interrupted transfers when the provider supports partial files or remote offsets
--bucket <name>S3 bucket name
--region <region>S3/Azure region
--container <name>Azure container name
--include <pattern>Include only files matching glob (repeatable)
--exclude-global <pattern>Exclude files matching glob (repeatable)
--include-from <file>Read include patterns from file
--exclude-from <file>Read exclude patterns from file
--min-size <size>Min file size filter (100k, 1M, 1G)
--max-size <size>Max file size filter
--min-age <duration>Skip files newer than (7d, 24h)
--max-age <duration>Skip files older than
--max-transfer <size>Stop after N bytes transferred (10G, 500M). Exit code 8, "stopped at a limit and nothing failed"; on sync a timeout that fails a transfer reports 4 instead. With --json the budget is named by an over_budget count of the files it left behind
--retries <n>Retry failed transfers N times (default: 3)
--retries-sleep <dur>Delay between retries (5s, 1m, 500ms). Default: 1s
--max-backlog <n>Max queued parallel tasks (default: 10000)
--files-from <file>Transfer only files listed in file (one per line, # comments)
--files-from-raw <file>Like --files-from but preserves whitespace and empty lines
--immutableNever overwrite existing files on destination. Since v4.2.1, an existing destination of a different size is refused as an error (exit 4) rather than skipped - it is most often the partial an interrupted upload left - and one of the same size is counted as skipped
--no-check-destSkip remote listing during sync (assume empty destination)
--max-depth <n>Maximum recursion depth for ls, find, sync, get -r, put -r
--default-time <ts>Fallback mtime when backend returns None (ISO 8601, RFC 3339, or now)
--fast-listS3 only: recursive listing in a single API call
--inplaceWrite downloads directly to final path (no .aerotmp temp file)
--chunk-size <size>Override upload chunk size (e.g., 64M). Min 5M for S3
--buffer-size <size>Override download buffer size (e.g., 256K, 1M)
--dump <kinds>Debug: headers, bodies, auth (comma-separated)

Bandwidth Schedule (--bwlimit) ​

The --bwlimit flag accepts either a plain rate (1M, 500K, 2G, ...) or a time-of-day schedule in the rclone-compatible format:

"HH:MM,RATE HH:MM,RATE ..."

Each entry is a comma-separated pair: a time of day (24-hour HH:MM, local time) and a rate. Use the literal off (or 0) to mean "unlimited". Whitespace separates entries; entries can be in any order, they are sorted internally before lookup.

bash
# Throttle to 512 KB/s during business hours, ramp up to 10 MB/s at lunch,
# go full-speed after work, completely off overnight
aeroftp-cli sync --profile "backup" ./local /remote --bwlimit "08:00,512k 12:00,10M 18:00,off 23:00,off"

How the active rate is resolved ​

At runtime the active rate is the last entry whose time is <= now. If now is earlier than the first scheduled entry of the day, the schedule wraps around midnight and the rate of the last entry of the day stays in effect. So for "09:00,1M 18:00,off" the rate is off between midnight and 09:00.

Example with hours of the day on a single line:

   00:00         09:00          18:00            24:00
       │           │               │                │
       │   off    │   1 MB/s    │     off        │
       │ (wrap   ) │ (active)    │ (active)       │

Local time, not UTC ​

Times are interpreted in the local timezone of the machine running the CLI (matching rclone's behavior). On a host in CET, 08:00 means 8 AM Central European Time, not UTC. This is a deliberate choice for ergonomic scheduling.

Validation ​

  • HH must be in 0..=23, MM must be in 0..=59. Malformed entries (e.g. 25:00,1M or 10:99,2M) are silently skipped, they cannot brick a schedule because the parser treats them as no-op.
  • A schedule with only a plain rate and no , is treated as a global cap (--bwlimit "1M").
  • A schedule with only malformed entries falls back to "no limit" rather than crashing.

Limitation: rate is resolved once per command ​

The active rate is computed when the command starts. A long-running sync --watch that crosses a scheduled boundary (e.g. starts at 17:55 with "18:00,off") will not automatically downshift at 18:00 in the current implementation. If this matters for your workflow, file a feature request: the design is documented in APPENDIX-U §13.2 and the live re-poll is a 1-2h follow-up.

Output Hygiene ​

The CLI follows Unix conventions: stdout carries data only (file listings, content, JSON), stderr carries messages (progress bars, summaries, connection status). This makes piping safe:

bash
aeroftp-cli ls sftp://user@host / --json 2>/dev/null | jq '.entries[].name'
aeroftp-cli cat sftp://user@host /data.csv > output.csv 2>/dev/null

Respects NO_COLOR, CLICOLOR, and CLICOLOR_FORCE environment variables.

Exit Codes ​

CodeMeaning
0Success
1Connection / network error
2Not found
3Permission denied
4Transfer failed or partial
5Configuration / usage error
6Authentication failed
7Not supported by protocol
8Stopped at a limit and nothing failed: a timeout on most commands, the --max-transfer budget on sync (with --json, a reached budget reports over_budget)
9Already exists / directory not empty (--immutable, --no-clobber)
10Server error / parse error
11I/O error
99Unknown error
130Interrupted by SIGINT (Ctrl+C). Since v4.2.1 this covers every transfer phase: sync, recursive and glob get/put, and single-file get/put/pget, with "status": "interrupted" in JSON output. A second Ctrl+C force-exits

CI/CD Example ​

yaml
# GitHub Actions deployment
- name: Deploy to server
  env:
    DEPLOY_PASS: ${{ secrets.DEPLOY_PASSWORD }}
  run: |
    echo "$DEPLOY_PASS" | aeroftp-cli --password-stdin put \
      sftp://deploy@prod.example.com ./dist/ /var/www/ -r

For OAuth providers in CI, use --profile with the vault pre-configured on the runner:

bash
AEROFTP_MASTER_PASSWORD=${{ secrets.VAULT_PW }} \
  aeroftp-cli sync --profile "Production S3" ./build/ / --delete

Recent Highlights ​

  • v3.6.6 - Agent surface, onboarding polish, server SpeedTest: new agent-connect (single-shot connect / capabilities / quota / path), agent-info --json protocol_features + agent_connect_supported_protocols, head -c / --bytes byte-range preview, ls --limit / --files-only / --dirs-only (same trio on find plus --name <glob>), mkdir --json already_existed: bool, per-profile auth_state (valid / expired / needs_refresh / no_credentials / unknown), speed-compare multi-server benchmark with JSON v1 / CSV / MD reports + password redaction. Cross-profile multi-select on My Servers and a new IconPicker dialog ship in the GUI counterpart.
  • v3.6.5 - Throughput parity with rclone, FTP home-directory fix, OAuth profile rename: plain-FTP TLS-drain bypass (-74% on 100 MiB Docker FTP), SFTP buffer raised to 256 KiB (-36%), WebDAV PUT body 8 KiB → 256 KiB (-33%); FTP CLI now respects the server-provided home directory after login (no spurious CWD /); OAuth saved servers can be renamed from the Edit dialog (issue #127); find glob no longer matches as substring across 7 cloud providers; mkdir + put works on empty-prefix object stores; Dropbox missing-file detection drops the failure latency from ~3.5 s to ~150 ms; Koofr WebDAV preset path fix (issue #126); pget is now a real subcommand; banner cleanup; 39 default_value = "_" arguments now hide the [default: _] sentinel.
  • v3.6.4 - Windows keystore export fix, autostart UX, update opt-out: os error 5 on Windows export resolved (write handle kept through flush + sync_all); "Start minimized to tray" + "Don't check for updates" toggles; NSIS post-install hooks now actually run (CUSTOM_PRE_INSTALL macro names corrected) so HKCU PATH registration finally activates on every install (issue #125); silent uninstall preserves user data on WinGet upgrades.
  • v3.6.1 - Windows first-class delta sync: AeroFTP ships aerorsync, a native rsync protocol 31 implementation in pure Rust. No rsync.exe bundle, no WSL requirement, byte-identical to stock rsync 3.4.1 in CI. See AeroRsync for the architecture and Delta Sync for the user-facing UI.
  • v3.5.4 - MCP hardening: top-level aeroftp-cli mcp, vault auto-init in MCP, per-profile serialization, schema validation, S3 bucket fix from vault profiles, FTP/SFTP/WebDAV/Filen/FileLu/Drime/Immich error message hardening.
  • v3.5.3 - sync --watch: continuous bidirectional sync with native filesystem watcher (inotify / FSEvents / ReadDirectoryChangesW), anti-loop cooldown, NDJSON output. First CLI on the market with this feature natively (rclone doesn't ship it).
  • v3.5.3 - Agent-friendly flags: --files-from, --immutable, --no-check-dest, --max-depth, --inplace, --fast-list (S3), --compare-dest / --copy-dest, cleanup for orphan .aerotmp.
  • v3.5.2 - Determinism: 12 structured exit codes mapping all ProviderError variants, mkdir --parents, rm --force, put --no-clobber, --chunk-size / --buffer-size overrides.
  • v3.3.4 - Local server bridges: serve http, serve webdav, serve ftp, serve sftp.

Test Results (v3.5.3) ​

376 unit tests, all passing. Run with cargo test in src-tauri/.

Provider Coverage (116 tests) ​

ProviderTestsAreas
GitHub55Client, errors, GraphQL, rate limiting, releases, repo mode, branches, paths
MEGA19AES-CBC/CTR/ECB, chunk MAC, KDF v2, node keys, base64, native API, paths
Yandex Disk7Path encoding, resource entries, path resolution
FileLu6Deserialization, MIME detection, path normalization
FTP5DOS/Unix/MLSD listing parsing
Koofr4Config validation, timestamps, path splitting
WebDAV3XML properties, URL building, PROPFIND parsing
SFTP3Permissions formatting, path normalization, provider creation
S32Path-style and virtual-hosted URL building
OAuth 1.03Nonce, percent encoding, timestamps
OAuth 2.02Google config, callback parsing
HTTP retry2Delay bounds, retryable status codes
Provider types4Default ports, MEGA config modes, entry extensions
Provider factory10Server field parsing (hostname, IP, IPv6, WebDAV paths, ports)

CLI Binary (90 tests) ​

AreaTestsDetails
URL parsing10FTP, SFTP, S3, WebDAV, MEGA, Koofr, OpenDrive, GitHub (incl. branch suffix, token)
pget (parallel download)10Chunk planning, assembly, binary integrity, temp cleanup, edge cases
serve http11Path sanitization, traversal prevention, URI decoding, null byte injection, bind validation
serve webdav5PROPFIND XML, file/dir entries, destination parsing, path building
Range requests5Normal, open-end, suffix, clamped, invalid
Speed limits5MB, KB, bytes, case-insensitive, invalid
Path validation5Traversal, null bytes, Windows drives, dotfiles, relative paths
Service auth6Loopback vs remote tokens, bearer/basic auth, credential generation
S3 profile defaults3Google preset, Wasabi template, existing endpoint preservation
Filename sanitization3Normal, ANSI escape removal, control characters
Profile options2Tencent COS path style, camelCase-to-snake_case
Tool policy2Danger levels, metadata/preview/exec distinction
Update checks224-hour frequency, invalid timestamp recovery
Release versioning2v-prefix stripping, semver comparison
Output formatting2Size (bytes to GB), speed
Provider port detection2Server info parsing, protocol fallback
Error exit codes1Provider error mapping
Agent path confinement1Initial path restriction
HTML escaping1XSS prevention
Served path building1Backend path with traversal confinement

Core Engine (170 tests) ​

ModuleTestsDetails
rclone crypt23EME encrypt/decrypt, name encrypt/decrypt, key derivation, chunk nonces, PKCS7, end-to-end
sync18Error classification, file comparison, journal, path portability, snapshots, templates, filters
sync scheduler17Time windows, day filters, overnight carry-over, interval validation, weekday logic
delta sync12Block sizing, signatures, rolling checksums, delta reconstruction, serialization
file watcher12Lifecycle, exclusions (git, OS, temp), event mapping, inotify capacity, modes
cross-profile transfer12Path mapping, time parsing, plan generation, request serialization
transfer pool10Compression modes, config validation, parallel sync results, extension detection
rclone import10INI parsing, FTP/FTPS/WebDAV mapping, obscure/reveal, export
MCP server12Initialize, transcript, tools/resources/prompts listing, request ID handling, cancel keys
cloud config5Default config, validation, conflict strategy, protocol backward compat
sync ignore5Patterns, comments, directory-only, negation, empty files
speech (STT)6Language normalization, WAV validation (mono/16kHz), transcription
provider commands8Remote URL matching (SSH, HTTPS, case-insensitive, boundary, collision)
transfer domain3Error redaction, timeout mapping, unknown kind fallback
agent memory DB2Prompt injection sanitization, content stripping
FTP transfer executor2Remote path splitting
session manager1Session lifecycle
session pool1Pool config validation
transfer settings1Serialization roundtrip

Live Provider Tests ​

All commands tested live against 12 providers via --profile:

ProviderProtocolconnectlsput/gethead/tailhashsumcheckaboutdf
WD MyCloud NASSFTPPASSPASSPASSPASSPASSPASSPASSPASS
axpdev.itFTPPASSPASSPASSPASSPASSPASSPASSPASS
PlaygroundGitHubPASSPASSPASSPASSPASSPASSPASSPASS
MEGA.nzMEGAPASSPASSPASSPASSPASSPASSPASSPASS
OpenDriveOpenDrivePASSPASSPASSPASSPASSPASSPASSPASS
FilenFilen (E2E)PASSPASSPASSPASSPASSPASSPASSPASS
KoofrWebDAVPASSPASSPASSPASSPASSPASSPASSPASS
KoofrNative APIPASSPASSPASSPASSPASSPASSPASSPASS
WD MyCloud NASWebDAVPASSPASSPASSPASSPASSPASSPASSPASS
Backblaze B2S3PASSPASSPASSPASSPASSPASSPASSPASS
AzureAzure BlobPASSPASSPASSPASSPASSPASSPASSPASS
4sharedOAuth 1.0PASSPASSPASSPASSPASSPASSPASSPASS

Additional commands tested on SFTP: mkdir, rm, mv, cat, stat, find, tree, touch, dedupe, track-renames, bwlimit - all PASS.

Filter system (--include, --exclude-global, --min-size, --max-size) and check with hashsum round-trip verification all passed on all providers.

v3.5.3 Flag Validation & Stress Tests ​

46 targeted tests across 5 servers (FTP Docker, WebDAV Docker, SFTP Docker, S3 iDrive, SFTP NAS WD MyCloud). All PASS.

CategoryTestsServersResult
Safety guards (--no-check-dest + --delete, --immutable + --no-check-dest)2FTPPASS
--files-from (missing file, >10MB, filtering)4FTPPASS
--default-time (invalid, naive, now, RFC 3339 Z, RFC 3339 +offset)5FTPPASS
--immutable put single (skip existing)4FTP, WebDAV, S3, NASPASS
--immutable get (skip existing local)4FTP, WebDAV, S3, NASPASS
--immutable put -r (upload new, skip existing)4FTP, WebDAV, S3, NASPASS
--no-check-dest sync4FTP, WebDAV, S3, NASPASS
--fast-list (S3 recursive, FTP fallback)2S3, FTPPASS
--inplace download4FTP, WebDAV, S3, NASPASS
cleanup dry-run + --force2FTP, WebDAVPASS
rm -r multi-level (3-4 levels deep)3FTP, WebDAV, NASPASS
Bisync --conflict-mode rename3FTP, WebDAV, S3PASS
Stress: 50-file batch upload (parallel 8)1FTP DockerPASS (0.8s)
Stress: 30-file batch upload (parallel 4)1S3 iDrivePASS (20.5s)
Stress: bisync with 10 simultaneous conflicts1FTP DockerPASS
dedupe --mode list1FTPPASS
--max-depth find1FTPPASS

Advanced Features (v3.4.8) ​

56 tests on 9 providers (Storj S3, Cloudflare R2, FTP Aruba, jianguoyun, InfiniCloud, Koofr, CloudMe, MEGA, 4Shared):

FeatureTestsPassedProviders
mount (FUSE r/w)1111S3, R2, FTP, WebDAV x4, MEGA
serve ftp66S3
serve sftp11S3
daemon + jobs55-
bisync (snapshot, conflict modes)77S3
ncdu TUI44S3, R2, FTP
crypt overlay88S3
transfer controls1111S3, R2, FTP
serve http33S3, SFTP, FTP
Total5656all passing

aeroftp.app - Released under the GPL-3.0 License. AeroFTP Reviews