Skip to content

CLI

kutup is a Rust CLI for the same E2EE primitives as the web — register, login, account recovery, ls, upload (resumable), download, sync, trash, share, versions, devices, 2fa, public-link consumption, file/folder rename. All operations are end-to-end encrypted in your shell; the server only ever sees ciphertext.

Scripting-friendly: every command honors a global --json flag (stdout is exactly one JSON document; prompts/progress/status go to stderr, and with --json even errors are emitted as JSON on stderr), and exit codes are differentiated — 0 ok, 1 generic, 2 usage, 3 auth/session, 4 not found, 5 network/server. Destructive commands prompt [y/N] and take --yes for non-interactive use.

Build from source (Rust ≥ 1.91.1):

Terminal window
git clone https://github.com/kutupbt/kutup.git
cd kutup
cargo build --release -p kutup-cli # → target/release/kutup
install -m755 target/release/kutup ~/.local/bin/kutup

Tagged release binaries are built for Linux x86-64/ARM64, macOS Intel/Apple Silicon, and Windows x86-64 and published on GitHub Releases (see release workflow).

Terminal window
# Create an account (client-side crypto; prints a 24-word recovery phrase once).
kutup register --server https://your.kutup.host --email you@example.com --username you
# Login (interactive password prompt; password can also come from KUTUP_PASSWORD).
kutup login --server https://your.kutup.host --email you@example.com
# List your folders + files at the Drive root.
kutup ls
kutup ls <folder-id> # contents of a sub-folder
# Forgot the password? Reset it with the 24-word recovery phrase
# (KUTUP_RECOVERY_PHRASE + KUTUP_PASSWORD for non-interactive use).
kutup recover --server https://your.kutup.host --email you@example.com
# Upload a file. The CLI's chunked stream encryption (5 MB blocks via
# crypto_secretstream) has NO browser-imposed size limit — multi-GB
# files (ISOs, raw video, datasets) work where the web upload chokes
# around ~2 GB and crashes the tab. File size is bounded by disk,
# not RAM. Interrupted uploads RESUME from the last 5 MB chunk when
# you rerun the same command (--no-resume restarts from zero).
kutup upload ./big-dataset.tar.gz <folder-id>
kutup upload ./local-dir <folder-id> --recursive
# Download a file. For collab-edited files (notes / office /
# whiteboards) returns the latest snapshot — same content the web
# shows, not the cold-start initial. Whiteboard image assets are
# re-inlined so the .excalidraw is self-contained.
kutup download <file-id>
kutup download <file-id> ./local/path/
# Bidirectional sync: recursive (sub-folders ↔ sub-collections), with
# real change detection — local edits re-upload, remote changes
# re-download, and concurrent edits produce a name.sync-conflict-<ts>
# copy instead of silently overwriting. Deletions propagate only with
# --delete; --dry-run previews without touching anything.
kutup sync ./local-folder <folder-id>
kutup sync ./local-folder <folder-id> --watch # live fsnotify sync
kutup sync ./local-folder <folder-id> --watch --poll 60 # + poll remote every 60s
kutup sync ./local-folder <folder-id> --delete --dry-run
# Trash: deleted items are recoverable until the retention window ends.
kutup trash ls
kutup trash restore <id>
kutup trash empty --yes
# Rename a file or a folder (names are E2EE metadata; content untouched).
kutup mv <file-id> "new name.txt"
kutup mv <folder-id> "New folder name" --folder
# List versions. Restore is currently safe for CLI/sync-created files;
# live-collaboration snapshots need the web client's derived content-key path.
kutup versions list <file-id>
kutup versions restore <file-id> <version-id>
# Public-link consumption — no kutup login required for the URL itself.
kutup pub get https://your.kutup.host/s/<token>#key=<base64>
kutup pub download <url> <file-id>
# Discover the rest.
kutup --help
kutup version

The >2 GB path is the standout. Browser File API + Web Crypto streaming work in theory but practically wedge the tab at multi-GB sizes; the CLI streams crypto_secretstream (XChaCha20-Poly1305, 5 MB chunks) over a Rust reader, so it handles arbitrarily large files at constant ~5 MB memory.