Skip to content

Bebbssos/internxt-cli-rust

Repository files navigation

internxt-cli-rust (ixr)

A Rust port of Internxt's official CLI, aiming to be a fast, low-memory, single static binary with fully streaming transfers.

Works on any account type, including Free. The official CLI refuses to run unless your plan has server-side CLI access (bundled with Ultimate); ixr skips that check.

Everything else about your plan still applies — this only removes the CLI-access gate. In particular: the per-file upload cap from /files/limits (see Upload size limit), and Free/legacy plans rejecting empty (0-byte) files outright (HTTP 402).

Not affiliated with or endorsed by Internxt.

Written mostly by Claude Code, porting the behaviour of Internxt's official Node/TypeScript packages.

The Drive engine (auth, crypto, transfers) lives in a separate crate, internxt-core — this repo is the CLI front-end built on top of it.

Contents

Install

  • Docker: docker run ghcr.io/bebbssos/ixr <command> — see docs/DOCKER.md for compose examples (serving WebDAV/SMB/SFTP at once, one-shot upload containers, etc).

  • Cargo: cargo install internxt-cli (crate name; the installed binary is still ixr), or cargo binstall internxt-cli for a prebuilt binary with no local compile. Plain cargo install only builds the default feature set (SSO, WebDAV over HTTP, FUSE) — the prebuilt binaries above ship with almost every feature on, so to match them pass --features explicitly, e.g. cargo install internxt-cli --features webdav-tls,smb,nfs,sftp,termimage,self-update. See Features for the full flag list. self-update is deliberately never a default — see its entry in Features for why.

  • AUR (Arch Linux): ixr-bin, e.g. yay -S ixr-bin.

  • Prebuilt binary: download an archive from the releases page for your platform, or grab the latest directly:

    OS Downloads
    Windows x86_64 · ARM64
    Linux (glibc) x86_64 · ARM64
    Linux (musl) x86 · ARMv7 · ARMv6
    macOS Apple Silicon · Intel
    FreeBSD x86_64

    These links always resolve to the latest stable release. Every build ships with the full feature set (SSO, WebDAV+TLS, SMB/NFS/SFTP servers, self-update, in-terminal thumbnails, FUSE/WinFSP mount) except: macOS drops FUSE (needs macFUSE headers to build; no verified unattended way to get those onto a CI runner yet), and Windows drops in-terminal thumbnails (no Kitty/iTerm2 graphics protocol on Windows). See FUSE/mount support for the full build/runtime matrix. ixr update self-updates in place afterwards (standalone-binary installs only — the other methods above manage updates themselves).

  • From source: see Build below.

Build

cargo build --release
# binary at target/release/ixr (SSO + WebDAV over HTTP + FUSE enabled by default)

# stack multiple features on top of the defaults with a comma-separated list —
# here: HTTPS for WebDAV, plus the SMB and SFTP serve backends (all off by default):
cargo build --release --features webdav-tls,smb,sftp

# --no-default-features drops the defaults (sso, webdav, fuse, dotenv)
# instead of adding to them — combine with --features to build a minimal
# binary with only what you want, e.g. WebDAV-only, no SSO/FUSE:
cargo build --release --no-default-features --features webdav

See Features below for what each --features/--no-default-features flag enables and disables.

A multi-arch Docker image is available — see the Dockerfile at the repo root for how it's built, or docs/DOCKER.md for usage examples.

Features

Cargo feature flags gate optional command surface, mainly to keep the default binary small and dependency-light. default = ["sso", "webdav", "fuse", "dotenv"].

Feature Default Enables Notes
dotenv on Loads a .env file from the current directory at startup, before arg/env parsing Lets IXR_* env-backed flags live in .env instead of the real environment. Never overrides vars already set in the environment. Pulls in dotenvy.
sso on Web-based SSO flow for login/login-sso (local callback server + browser launch) Without it, login falls back to the legacy flow and login-sso errors. Pulls in axum + open.
webdav on serve webdav over plain HTTP Pulls in axum + tokio-util + mime_guess.
webdav-tls off HTTPS for serve webdav (--webdav-https) Requires webdav. Pulls in axum-server + rustls-pemfile + rcgen (self-signed or your own cert/key).
fuse on mount, serve fuse One feature, all platforms — picks fuser (Unix) or winfsp_wrs/WinFSP (Windows) per target_os. Build/runtime requirements differ sharply by OS; see FUSE/WinFSP mount support.
smb off serve smb — SMB2/3 share Experimental. All platforms. Built on a fork of the smb-server crate.
nfs off serve nfs — NFSv3 export Experimental. All platforms.
sftp off serve sftp — SFTP over SSH Experimental. All platforms. Pulls in russh + russh-sftp.
termimage off thumbnail display — inline terminal image rendering Pulls in viuer + image. Kitty/iTerm2 graphics protocol, with a Unicode half-block fallback.
self-update off ixr update — replace the running binary with the latest GitHub release Off by default — a self-built binary should update by rebuilding. The GitHub release workflow turns it on for every standalone-binary target; AUR's ixr-bin reuses that binary so it has it too, while Docker builds its own binary without it. Pulls in self_update + semver.

FUSE/WinFSP mount support

mount/serve fuse is one Cargo feature (fuse) everywhere, but the library backing it — and what it needs to build and run — differs sharply by OS. None of these are things ixr can bundle into the binary: they're kernel-level filesystem drivers, so the OS driver package has to be installed on the machine regardless of how ixr itself was built or installed (same constraint every FUSE-based tool has — rclone, sshfs, etc.).

OS To build To run Notes
Linux (glibc & musl) Nothing extra — fuser's pure-rust mount path talks to /dev/fuse directly, no libfuse headers needed. fuse3 package (provides /dev/fuse + the fusermount3 helper); most distros ship it, install via your package manager (e.g. apt install fuse3, dnf install fuse3, pacman -S fuse3). CI-verified on Linux x86_64/ARM64 (glibc) and the three musl cross targets.
macOS macFUSE headers (provides the fuse.pc pkg-config file fuser's build.rs probes for). macFUSE, plus approving its kernel extension once in System Settings → Privacy & Security (a GUI step — can't be scripted). Not currently in the official release binaries — no unattended, non-hanging way to get macFUSE's headers onto a macos-latest CI runner has been verified yet. Builds fine from source once macFUSE is installed locally: brew install --cask macfuse && cargo build --release --features fuse.
FreeBSD Nothing extra — same pure-rust path as Linux. fusefs-libs3 (pkg install fusefs-libs3) + kldload fusefs if the module isn't auto-loaded. Official release binaries build with fuse enabled.
Windows WinFsp installed (its build.rs reads the WinFsp\InstallDir registry key) — build must run natively on Windows, this cannot be cross-compiled from another OS. WinFsp installed (the driver + winfsp-x64.dll, delay-loaded so ixr finds it via the registry rather than needing it next to the exe). Uses winfsp_wrs (MIT), not SnowflakePowered's winfsp/winfsp-sys crates (GPL-3.0 — wrong license for this project). Mount target can be a drive letter (X:) or an empty directory.

If the driver is missing at runtime, mount/serve fuse doesn't crash — it prints a normal ✕ Error: failed to mount at <path>: ... and exits 1, with a hint pointing back to this table when the underlying error looks like a missing-driver situation (as opposed to e.g. a bad mountpoint path).

Global flags

Every command accepts:

  • --json — print a single JSON result object and suppress progress/status output. See each command's "JSON output" below for its shape.
  • -x, --non-interactive (env IXR_NONINTERACTIVE) — never prompt for input; error out instead when a required value is missing.
  • --no-timeout (env IXR_NO_TIMEOUTS) — disable the idle-read timeout on network transfers (uploads/downloads). Use if a slow --stdin producer or --stdout consumer trips a false timeout on an otherwise-healthy transfer over a slow link. Connect timeout stays on regardless — a hung connection attempt is unrelated to transfer speed and should still fail fast.

IDs are Drive UUIDs. Most commands that take an id also accept a --path (or --dest-path / --remote-path) alternative — give one or the other, not both. Where a destination/parent folder id is optional, leaving it empty targets your root folder (or workspace root, if a workspace is active).

Commands

The space-separated form (ixr upload file) is the primary syntax. Every command grouped under a parent below also has an equivalent flat/hyphenated alias (ixr upload-file) for drop-in compatibility with the official CLI — both always work. Parent rows (in bold) just print help for their group.

Command Description Aliases Notes
login Log in — alias for login-sso (or login-legacy without sso). Matches official's SSO-only login.
login-legacy Log in with email + password.
login-sso Force the web-based SSO flow. New; needs sso (default on).
logout Log out the current/targeted account.
whoami Show the active/targeted account.
accounts Manage logged-in accounts New — no official equivalent.
  accounts list List every logged-in account.
  accounts switch Switch the active account.
usage Plan, used space, upload limit. account, account-info New — no official equivalent.
list List a folder's contents.
create Create a folder
  create folder Create a folder. create-folder
upload Upload a file or folder
  upload file Upload a single file (streaming). upload-file
  upload folder Recursively upload a folder tree. upload-folder
download Download a file or folder
  download file Download + decrypt a file. download-file
  download folder Recursively download a folder tree. download-folder New — official downloads single files only.
move Move a file or folder
  move file Move a file into a folder. move-file
  move folder Move a folder into a folder. move-folder
rename Rename a file or folder
  rename file Rename a file. rename-file
  rename folder Rename a folder. rename-folder
trash Manage the trash
  trash file Move a file to the trash. trash-file
  trash folder Move a folder to the trash. trash-folder
  trash list List the trash contents. trash-list
  trash restore file Restore a trashed file. trash-restore-file
  trash restore folder Restore a trashed folder. trash-restore-folder
  trash clear Empty the trash permanently. trash-clear
delete Delete a file or folder
  delete file Trash a file (--permanent to hard-delete). delete-file New — trash alias + --permanent.
  delete folder Trash a folder (--permanent to hard-delete). delete-folder New — trash alias + --permanent.
  delete permanently file Permanently delete a file. delete-permanently-file
  delete permanently folder Permanently delete a folder. delete-permanently-folder
workspaces Manage workspaces
  workspaces list List your workspaces. workspaces-list
  workspaces use Set the active workspace. workspaces-use
  workspaces unset Unset the active workspace. workspaces-unset
sync One-way sync New — no official equivalent.
  sync up Push: local → remote. sync-up
  sync down Pull: remote → local. sync-down
serve Serve Drive over WebDAV/FUSE/SMB/NFS/SFTP (foreground). Needs ≥1 of webdav,fuse,smb,nfs,sftp. WebDAV mirrors official; rest new.
mount Mount Drive as a local FS via FUSE/WinFSP. New; needs fuse (default on).
id-from-path Print the uuid at a Drive path. get-id New — no official equivalent.
path-from-id Print the Drive path of a uuid. get-path New — no official equivalent.
thumbnail Manage a file's thumbnail thumbnails New — official auto-generates only.
  thumbnail generate (Re)generate from the file's image.
  thumbnail upload Upload a custom thumbnail image.
  thumbnail download Download the current thumbnail.
  thumbnail display Render inline in the terminal. Needs termimage (default off).
update Update the running binary to the latest GitHub release. New — no official equivalent. Standalone-binary installs only; needs self-update (off by default, on in the prebuilt release binaries).

Command reference

login

Logs in. An alias for login-sso when built with the sso feature (default, matching the official CLI's SSO-only login); otherwise an alias for login-legacy. Use login-sso or login-legacy directly to force a specific flow.

Flags: --host <HOST>, --port <PORT> (SSO callback server address/port, default 127.0.0.1 / a random free port); -e/--email, -p/--password, -w/--twofactor, -t/--twofactortoken (legacy flow). The set that applies to the active flow is used; the others are accepted but ignored. Plus --add/--replace (see Multiple accounts).

ixr login                                  # SSO: opens a browser to authenticate
ixr login --host 0.0.0.0 --port 4000       # cross-device SSO (e.g. inside a container)

JSON output: { "success": true, "message": "...", "login": <credentials> } on success. login (JSON credentials, tokens, keys) is sensitive — treat it like a secret. On failure: { "success": false, "message": "..." }.

login-legacy

Same command name as the official CLI's login-legacy. Logs in with email + password (+ 2FA if the account requires it). Prompts for any missing value unless -x/--non-interactive.

Flags: -e/--email, -p/--password, -w/--twofactor, -t/--twofactortoken (takes priority over --twofactor when both are given); --add/--replace (see Multiple accounts).

ixr login-legacy --email you@example.com     # prompts for password (+ 2FA)
ixr login-legacy -e you@example.com -p '...' -w 123456
ixr login-legacy -e another@example.com -p '...' --add   # keep both accounts

JSON output: same shape as login.

login-sso

New — no official equivalent (the official CLI's plain login is already SSO-only, so it has no separate login-sso). Forces the web-based SSO flow. Errors if built without the sso feature.

Flags: --host <HOST>, --port <PORT>, --add/--replace (see Multiple accounts).

JSON output: same shape as login.

logout

Invalidates the session server-side and removes the resolved account (the one targeted by IXR_USER, else the active one — see Multiple accounts) from local storage. If it was the active account and other accounts remain, none of them is auto-selected — run accounts switch to pick one.

Flags: --all (log out of every stored account instead of just the resolved one).

ixr logout
ixr logout --all

JSON output: { "success": true, "message": "User logged out successfully." }, or { "success": false, "message": "No user is currently logged in." }. With --all: { "success": true, "message": "Logged out of all accounts.", "accounts": [...] }.

whoami

Shows the resolved account (IXR_USER, else the active one). Refreshes the session token if it's near expiry; if the session is dead, removes that account from local storage (matching the official CLI's behaviour of logging out on a dead session) without touching any other stored account.

JSON output: { "success": true, "message": "...", "login": <credentials> }, or { "success": false, "message": "You are not logged in." }.

accounts list

New — no official equivalent (the official CLI supports one account at a time). Lists every account currently logged in on this machine, marking the active one with * in the human-readable view.

No flags.

ixr accounts list

JSON output: { "success": true, "accounts": ["a@example.com", "b@example.com"], "active": "a@example.com" }.

accounts switch

New — no official equivalent. Sets the active account for subsequent commands (until changed again, independent of any IXR_USER override on a given invocation — see Multiple accounts).

Flags: -e/--email <EMAIL> (omit for an interactive picker; errors in --json/-x mode if omitted).

ixr accounts switch -e b@example.com
ixr accounts switch                    # interactive picker

JSON output: { "success": true, "active": "b@example.com" }.

usage

Aliases: account, account-info. Not an official CLI command — it fans out the same drive-gateway endpoints the official CLI uses internally (/users/usage, /users/limit, /files/limits) plus a best-effort plan lookup on the payments API.

Plan:               Free
Used:               3.89 TB / 10 TB (38.9%)
  Drive:            3.89 TB
  Backups:          0 B
Space limit:        10 TB
Upload file limit:  10 GB

The plan name reads Tier (Type) (e.g. Pro (Subscription)), collapsing to one value when they agree. Legacy lifetime accounts show Free (Lifetime) — the tier endpoint mislabels old plans as free, but (Lifetime) still signals it's a paid plan; the space limit is always correct. If the payments API is unreachable the plan shows unknown.

JSON output:

{
  "success": true,
  "usage": {
    "plan": "Pro (Subscription)",
    "planLabel": "pro",
    "subscriptionType": "subscription",
    "used": 123456789,
    "drive": 123000000,
    "backups": 456789,
    "spaceLimit": 1000000000000,
    "spaceLimitInfinite": false,
    "usedPercent": 12.3,
    "uploadFileLimit": 10737418240
  }
}

list

Lists a folder's contents.

Flags: -i/--id <FOLDER_ID> (default: root), -p/--path <PATH> (alternative to --id), -e/--extended (adds created/modified date + size to the human-readable view).

ixr list -e                            # root folder, extended view
ixr list -i <folder-uuid> --json       # machine-readable output
ixr list -p /Documents/Reports

JSON output: { "success": true, "list": { "folders": [...], "files": [...] } } — always the full (non-extended) item objects, regardless of --extended (that flag only affects the human-readable table).

create folder

Also invocable via the flat alias create-folder. Creates a folder.

Flags: -n/--name <NAME> (required), -i/--id <PARENT_ID> (default: root), -p/--path <PATH> (alternative to --id).

ixr create-folder -n "Reports" -i <parent-uuid>
ixr create-folder -n "Reports" -p /Documents

JSON output: { "success": true, "folder": <DriveFolderData> }.

upload file

Also invocable via the flat alias upload-file. Uploads a single file (streaming; single-part or multipart depending on size).

Flags: -f/--file <PATH> (omit when using --stdin), -i/--destination <FOLDER_ID> (default: root), --dest-path <PATH> (alternative to --destination), --stdin (read the body from stdin instead of --file, requires --name), -n/--name <NAME> (Drive filename; required with --stdin, otherwise overrides the name/extension from --file's path), -s/--size <BYTES> (exact stdin length — streams directly if given, otherwise stdin is spooled to a temp file to learn its size), plus the upload-limit flags.

ixr upload-file -f ./file.bin -i <folder-uuid>
ixr upload-file -f ./big.iso --max-upload-size 20GB     # override the per-file cap
ixr upload-file -f ./big.iso --no-upload-limit          # disable the cap
tar -c ./dir | ixr upload-file --stdin --name dir.tar --dest-path /Backups

A thumbnail is generated automatically for image sources (best-effort, never fails the upload) — see thumbnail.

JSON output: { "success": true, "file": { "uuid": "..." } }.

upload folder

Also invocable via the flat alias upload-folder. Recursively uploads a folder tree (concurrent file uploads).

Flags: -f/--folder <PATH> (required), -i/--destination <FOLDER_ID> (default: root), --dest-path <PATH> (alternative to --destination), --exclude-empty-files (skip 0-byte files client-side instead of uploading them — see below), plus the upload-limit flags.

ixr upload-folder -f ./my-folder                # -i for a destination folder
ixr upload-folder -f ./my-folder --dest-path /Backups
ixr upload-folder -f ./my-folder --exclude-empty-files   # skip 0-byte files

Empty (0-byte) files are included by default and uploaded like any other file. Internxt's free/legacy plans reject them server-side (HTTP 402 Payment Required); on Ultimate/paid plans they upload fine. If any file fails for any reason — including that 402 — the command exits non-zero and reports which file and why, instead of silently omitting it. Pass --exclude-empty-files to skip 0-byte files up front and avoid that failure entirely on plans that don't support them.

JSON output: { "success": true, "folder": { "uuid": "..." }, "totalBytes": N, "uploadTimeMs": N } on full success. If one or more files failed to upload, the command instead exits with a non-zero status and an error message naming each failed file and its reason ({ "success": false, "message": "..." } in --json mode).

download file

Also invocable via the flat alias download-file. Downloads and decrypts a file, streaming to disk (or stdout).

Flags: -i/--id <FILE_ID>, -p/--path <PATH> (alternative to --id), -d/--directory <DIR> (default: current dir), -o/--overwrite, --stdout (write decrypted bytes to stdout instead of a file; status goes to stderr so it never mixes into piped data), --legacy-write (see below).

By default (when writing to disk, i.e. not --stdout), the download streams into a temp sibling file (.<name>.inxt-<random>.part, next to the destination) and is renamed into place only once it completes successfully; if anything fails partway (network drop, a bad chunk, Ctrl-C), the temp file is removed instead of being left behind. This means the destination path either doesn't exist or holds a complete file — never a silent truncated one. Pass --legacy-write to restore the old behavior (also the official CLI's behavior): write directly to the destination path with no cleanup on error, so an interrupted download can leave a partial file exactly at the filename you expected the complete download at.

ixr download-file -i <file-uuid> -d ./out --overwrite
ixr download-file -p /Documents/report.pdf -d ./out
ixr download-file -i <file-uuid> --stdout > file.bin
ixr download-file -i <file-uuid> -d ./out --legacy-write

JSON output: { "success": true, "path": "<local path>" } when written to disk. With --stdout, no JSON object is emitted at all (only a status line on stderr in non-JSON mode) — the file bytes own stdout instead.

download folder

Also invocable via the flat alias download-folder. New — the official CLI only downloads single files. Recursively downloads and decrypts a folder tree into a subfolder named after the Drive folder (it reuses the sync-down engine under the hood).

Flags: -i/--id <FOLDER_ID>, -p/--path <PATH> (alternative to --id), -d/--directory <DIR> (default: current dir — a subfolder named after the Drive folder is created inside it), -o/--overwrite (merge into an already-existing, non-empty destination folder).

ixr download-folder -i <folder-uuid> -d ./out
ixr download-folder -p /Documents/Reports --overwrite

JSON output: the sync-down result object (transferred, deleted, skipped, failed, actions, …).

move file / move folder

Also invocable via the flat aliases move-file/move-folder. Moves a file or folder into a destination folder.

Flags: -i/--id <ID>, -p/--path <PATH> (alternative to --id), -d/--destination <FOLDER_ID> (default: root), --dest-path <PATH> (alternative to --destination).

ixr move-file -i <file-uuid> -d <folder-uuid>
ixr move-folder -p /Old/Name -d <folder-uuid>

JSON output: move-file{ "success": true, "file": <DriveFileData> }. move-folder{ "success": true, "folder": <DriveFolderData> }.

rename file / rename folder

Also invocable via the flat aliases rename-file/rename-folder. Renames a file or folder (for files, name/extension are split automatically).

Flags: -i/--id <ID>, -p/--path <PATH> (alternative to --id), -n/--name <NAME> (required).

ixr rename-file -i <file-uuid> -n "new-name.txt"
ixr rename-folder -p /Old/Name -n "New Name"

JSON output: rename-file{ "success": true, "file": { "uuid", "plainName", "type" } }. rename-folder{ "success": true, "folder": { "uuid", "plainName" } }.

trash file / trash folder

Also invocable via the flat aliases trash-file/trash-folder. Moves a file or folder to the trash.

Flags: -i/--id <ID>, -p/--path <PATH> (alternative to --id).

JSON output: { "success": true, "file": { "uuid": "..." } } or { "success": true, "folder": { "uuid": "..." } }.

trash list

Also invocable via the flat alias trash-list. Lists the contents of the trash.

Flags: -e/--extended.

JSON output: { "success": true, "list": { "folders": [...], "files": [...] } } (same shape as list).

trash restore file / trash restore folder

Also invocable via the flat aliases trash-restore-file/trash-restore-folder. Restores a trashed file or folder into a destination folder.

Flags: -i/--id <ID>, -d/--destination <FOLDER_ID> (default: root), --dest-path <PATH> (alternative to --destination).

JSON output: { "success": true, "file": <DriveFileData> } or { "success": true, "folder": <DriveFolderData> }.

trash clear

Also invocable via the flat alias trash-clear. Empties the trash permanently — cannot be undone. Prompts for confirmation unless --force (required in --json/non-interactive mode).

Flags: -f/--force.

ixr trash-clear --force

JSON output: { "success": true, "message": "Trash emptied successfully." }.

delete permanently file / delete permanently folder

Also invocable via the flat aliases delete-permanently-file/delete-permanently-folder. Permanently deletes a file or folder — cannot be undone.

Flags: -i/--id <ID>.

JSON output: { "success": true, "message": "File permanently deleted successfully" } or { "success": true, "message": "Folder permanently deleted successfully" }.

delete file / delete folder

Also invocable via the flat aliases delete-file/delete-folder. New — a convenience alias that trashes a file or folder by default, or permanently deletes it with --permanent. The official CLI has delete permanently file|folder but no plain delete file|folder. Without --permanent this is equivalent to trash-file/trash-folder; with it, to delete-permanently-file/delete-permanently-folder.

Flags: -i/--id <ID>, -p/--path <PATH> (alternative to --id), --permanent (hard-delete instead of trashing — cannot be undone).

ixr delete-file -p /Documents/old.txt              # move to trash
ixr delete-folder -i <folder-uuid> --permanent     # hard-delete, no undo

JSON output: trashing → { "success": true, "file": { "uuid": "..." } } / { "success": true, "folder": { "uuid": "..." } }; with --permanent → the delete-permanently-* message shape above.

workspaces list

Also invocable via the flat alias workspaces-list. Lists the workspaces you belong to.

Flags: -e/--extended (owner, address, created-at in the human-readable view).

JSON output: { "success": true, "list": { "workspaces": [...] } } (always the full objects, regardless of --extended).

workspaces use

Also invocable via the flat alias workspaces-use. Sets the active workspace for subsequent commands — switches where drive calls and transfers route (its own bucket, network credentials and mnemonic).

Flags: -i/--id <WORKSPACE_ID> (use workspaces-list to find ids), -p/--personal (switch back to your personal drive space; conflicts with --id).

ixr workspaces-use -i <workspace-id>
ixr workspaces-use --personal

JSON output: { "success": true, "workspace": { "id", "name", "bucket", "rootFolderId" } }.

workspaces unset

Also invocable via the flat alias workspaces-unset. Unsets the active workspace (equivalent to workspaces-use --personal). No flags.

JSON output: { "success": true, "message": "Personal drive space selected successfully." }.

sync up / sync down

New — no official equivalent. A single one-way reconcile pass, then exit (not a daemon). The source side always wins — no bidirectional mode, no conflict resolution. Files are keyed by relative path; change detection compares size, then modification time (±2s tolerance). Downloaded files are stamped with the remote modification time so repeat sync-down runs are idempotent.

Flags (sync-up): -l/--local <DIR> (required), -r/--remote <FOLDER_ID> (default: root), --remote-path <PATH> (alternative to --remote), --delete[=trash|permanent] (opt-in; prunes extra remote files and folders — deleting the top-most extra folder cascades its subtree), --dry-run, --exclude-empty-files (skip 0-byte local files instead of uploading them — see below), plus the upload-limit flags.

Flags (sync-down): -l/--local <DIR> (required), -r/--remote <FOLDER_ID>, --remote-path <PATH>, --delete[=remove] (OS-trash delete mode not yet supported), --dry-run.

ixr sync-up   -l ./my-folder -r <folder-uuid> --dry-run   # preview a push
ixr sync-up   -l ./my-folder -r <folder-uuid> --delete    # push, trashing remote extras
ixr sync-up   -l ./my-folder -r <folder-uuid> --exclude-empty-files
ixr sync-down -l ./my-folder --remote-path /Backups       # pull new/changed files

sync-up uploads empty (0-byte) local files by default, same as upload folder. Internxt's free/legacy plans reject them server-side (HTTP 402 Payment Required), which counts as a normal per-file failure (reflected in failed below, and a non-zero exit code) rather than being silently skipped. Pass --exclude-empty-files to skip them client-side instead.

If failed is non-zero (for any reason, not just empty files), the command exits non-zero — check the actions list (or the non-JSON status lines) for which paths failed and why.

JSON output:

{
  "success": true,
  "dryRun": false,
  "transferred": 12,
  "deleted": 1,
  "skipped": 40,
  "failed": 0,
  "actions": [{ "action": "upload", "path": "notes.txt", "ok": true }]
}

serve

Runs one or more Drive backends in the foreground until Ctrl-C. Pass a comma-separated protocol list: webdav, fuse, smb, nfs, sftp. Running several at once shares one set of credentials, one folder-listing cache and one global upload limit.

The WebDAV backend mirrors the official CLI's WebDAV server; the official CLI runs it as a pm2-managed background service configured through a separate webdav-config command, while ixr runs it inline as a normal foreground command instead. FUSE, SMB, NFS and SFTP have no official equivalent — the official CLI only serves WebDAV. smb, nfs and sftp are experimental and off by default (build with --features smb/nfs/sftp).

Shared flags (bare): -i/--folder-uuid <UUID> (root to expose), -d /--delete-permanently (hard-delete instead of trash), --read-only, -v/--verbose (log every per-op request across all backends), --spool (spool uploads to a temp file before uploading; FUSE always spools), --spool-dir <DIR>, --max-concurrent-uploads <N> (0 = unlimited), --cache-ttl <SECS> (default 300 — matches rclone's own --dir-cache-time; also the FUSE kernel attr/entry TTL), --no-cache, --recent-window <BYTES> (default 4194304 — trailing-stream retention on the read path for FUSE/SMB/NFS/SFTP, see below; 0 disables it), plus the upload-limit flags.

Protocol-specific flags are prefixed:

  • WebDAV (--webdav-*): --webdav-host (default 127.0.0.1), --webdav-port (default 3005), --webdav-https (needs webdav-tls feature), --webdav-cert/--webdav-key (custom TLS cert/key, both required together), --webdav-timeout <MINS> (default 60; accepted but not yet wired to a request-timeout layer), --webdav-create-full-path (auto-create missing parent folders on PUT/MKCOL), --webdav-custom-auth
    • --webdav-username/--webdav-password (require HTTP Basic auth).
  • FUSE (--fuse-*): --fuse-mountpoint <DIR> (required when fuse is served — a directory on Unix, a drive letter like X: or a directory on Windows), --fuse-allow-other (Unix only — no WinFSP equivalent).
  • SMB (--smb-*): --smb-host (default 127.0.0.1), --smb-port (default 4445 — port 445 needs root/admin), --smb-share (default internxt), --smb-username (default internxt), --smb-password (omit for anonymous/guest — most clients, Windows especially, refuse it).
  • NFS (--nfs-*): --nfs-host (default 127.0.0.1), --nfs-port (default 12049 — port 2049 needs root/admin).
  • SFTP (--sftp-*): --sftp-host (default 127.0.0.1), --sftp-port (default 2022 — port 22 needs root/admin), --sftp-username (default internxt), --sftp-password (omit to accept any password), --sftp-host-key <PATH> (persistent host key; omit and one is generated once under ~/.ixr/sftp_host_key).
ixr serve webdav                                             # http://127.0.0.1:3005
ixr serve webdav --webdav-host 0.0.0.0 --webdav-port 8080     # accept LAN clients
ixr serve fuse --fuse-mountpoint ~/drive
ixr serve smb --smb-password secret                           # needs --features smb
ixr serve webdav,fuse --fuse-mountpoint ~/drive                # both at once, shared cache/creds
ixr serve webdav --read-only -i <folder-uuid>                  # read-only, rooted at a subfolder

WebDAV supported methods: OPTIONS, PROPFIND, GET/HEAD (with Range), PUT, MKCOL, DELETE, MOVE, LOCK/UNLOCK. COPY and PROPPATCH return 501 Not Implemented, matching the official server. DELETE trashes items by default (--delete-permanently for a hard delete).

serve/mount run until interrupted — there's no terminal JSON result object to speak of; --json mainly suppresses the startup/progress banner.

FUSE/SMB/NFS/SFTP each serve reads from one lazily-started decrypt stream per open file; a small backward/forward re-read (e.g. a media player re-visiting a container-index box — an MP4 moov atom, MKV cues — while probing a file) would otherwise force a full stream restart, a fresh network round trip. --recent-window <BYTES> keeps that many recently-streamed bytes per open file so those re-reads are served from memory instead; --recent-window 0 disables it (every non-sequential read restarts the stream, trading the per-file memory for none of the retention). WebDAV's GET is one-shot per HTTP request and doesn't use this.

Pass --verbose to dump each WebDAV request/response, headers included, to stderr.

mount

New — no official equivalent (the official CLI has no filesystem-mount mode). A thin wrapper over serve fuse where the shared flags use their bare names (no fuse- prefix).

Flags: -i/--folder-uuid <UUID>, --read-only, -d/--delete-permanently, --spool-dir <DIR>, --max-concurrent-uploads <N>, --cache-ttl <SECS> / --no-cache, --recent-window <BYTES> (see serve above), --allow-other (Unix only — no WinFSP equivalent), -v/--verbose, plus the upload-limit flags.

mkdir -p ~/drive && ixr mount ~/drive              # Ctrl-C to unmount (Unix)
ixr mount X: --read-only                           # drive letter (Windows)
ixr mount ~/drive -i <folder-uuid>                 # mount a subfolder as root

See FUSE/WinFSP mount support for what each platform needs to build and run this. Reads stream and decrypt lazily; writes buffer to a temp file and upload in full when the file is closed (Internxt has no partial-update API), replacing the old Drive entry.

id-from-path

Alias: get-id. New — no official equivalent. Prints the uuid of the Drive file/folder at a given path.

Flags: -p/--path <PATH> (required).

ixr id-from-path -p /Documents/report.pdf

JSON output: { "success": true, "uuid": "...", "isFolder": false, "type": "file" }.

path-from-id

Alias: get-path. New — no official equivalent. Prints the full Drive path of a file/folder given its uuid.

Flags: -i/--id <UUID> (required).

ixr path-from-id -i <uuid>

JSON output: { "success": true, "path": "/Documents/report.pdf", "isFolder": false, "type": "file" }.

thumbnail

Alias: thumbnails. New — the official CLI generates a thumbnail automatically on upload (which ixr also does) but has no user-facing management commands for it. Only image sources (jpg/png/webp/gif/tiff) are supported; PDF thumbnails are not generated (matching the official CLI).

Every subcommand takes -i/--id <UUID> or -p/--path <PATH> (one or the other) to identify the file.

  • thumbnail generate — regenerate a thumbnail from the file's own image content. JSON: { "success": true, "thumbnail": { "id": "...", "size": N } }.
  • thumbnail upload-f/--file <PATH> (required): upload a custom image as the thumbnail. --raw uploads it as-is instead of resizing to a 300x300 PNG. JSON: same shape as generate.
  • thumbnail download-d/--directory <DIR> (default: current dir), -o/--overwrite, --index <N> (0-based, for files with several thumbnails). JSON: { "success": true, "path": "<local path>" }.
  • thumbnail display (alias show, needs the termimage feature) — renders inline in the terminal (Kitty/iTerm2 graphics protocol, or a Unicode half-block fallback). --index <N>, -w/--width, -H/--height (max render size in terminal cells). Not meaningful with --json — it renders to the terminal rather than emitting a result object.
ixr thumbnail generate -p /Photos/cat.jpg
ixr thumbnail upload -i <file-uuid> -f ./custom-thumb.png
ixr thumbnail download -i <file-uuid> -d ./out
ixr thumbnail display -p /Photos/cat.jpg          # needs --features termimage

Automatic thumbnailing (on upload-file, upload-folder, and any serve backend write) can be disabled everywhere with IXR_THUMBNAILS=0.

update

New — no official equivalent. Needs the self-update feature (off by default; the GitHub release workflow enables it for every standalone-binary target, and AUR's ixr-bin reuses that same binary). Replaces the running binary in place with the latest GitHub release. Meant for the standalone binary distribution — a package-manager install (AUR) or a self-built binary (plain cargo install/cargo build) should still update via the package manager or a rebuild instead, since this would fight them for ownership of the file. Docker isn't affected either way — its image is built without the feature.

Only stable releases are considered by default; prerelease tags (e.g. v0.2.0-rc.1) are skipped unless --pre-release is given.

Flags: --check (report whether a newer release exists, without installing it), -y/--yes (skip the confirmation prompt — required under --json or --non-interactive), --pre-release (consider prerelease tags too).

ixr update --check
ixr update -y
ixr update --pre-release -y

Upload size limit

Uploads are validated against a per-file size cap before transferring — except there is no hard-coded default: when your plan sets no cap, uploads are unbounded. The cap is resolved in this order (first match wins):

  1. --no-upload-limit — disable the check entirely.
  2. --max-upload-size <SIZE> — a custom cap (5GB, 500M, 1073741824, … binary units).
  3. IXR_MAX_UPLOAD_SIZE env var — universal override for every upload command. A size string sets a cap; off/none/unlimited/0 disables it.
  4. Otherwise, your plan's maxUploadFileSize (from /files/limits; unlimited if unset).

These flags apply to upload-file, upload-folder, sync-up, and the serve/mount backends. Over-limit files are rejected up front (folder/sync uploads skip the offending file and continue; WebDAV PUT returns 413; FUSE/SMB/NFS/SFTP writes fail accordingly).

This is purely a local, client-side pre-check for fast failure — Internxt's servers independently enforce the same maxUploadFileSize on every upload. --no-upload-limit / --max-upload-size / IXR_MAX_UPLOAD_SIZE only change what ixr checks before sending; they cannot raise, bypass, or otherwise affect the server-side cap your plan actually has. An upload past the real server limit still fails server-side even with the local check disabled.

Configuration

API endpoints and app constants default to the public Internxt values (defined in the internxt-core crate's config module) and can be overridden via environment variables of the same name (DRIVE_NEW_API_URL, NETWORK_URL, PAYMENTS_API_URL, etc).

Credentials are stored AES-encrypted at ~/.ixr/credentials — its own directory, separate from the official CLI's ~/.internxt-cli. The file holds every logged-in account (see below), not just one.

Multiple accounts

Unlike the official CLI (one account at a time), ixr can hold several logged-in accounts in ~/.ixr/credentials and lets you pick which one a given command acts on.

Adding / replacing. login/login-legacy/login-sso only prompt or need a flag when a different account is already active:

  • Logging in again as the same active account just refreshes it (no prompt).
  • Logging in as a new account while a different one is active: interactively, you're asked to add (keep both, switch to the new one) or replace (log out the old one, switch to the new one); non-interactively (-x), pass --add or --replace explicitly or it errors.

Switching. accounts switch sets the active account for every subsequent command until changed again; accounts list shows what's stored and which one is active.

Targeting one account for a single command, without switching. Set IXR_USER=<email> — every command resolves credentials for that account instead of the active one, without persisting any change to accounts switch's active pointer. If that account isn't logged in yet, also set IXR_PASSWORD (and IXR_TWOFACTORCODE/IXR_OTPTOKEN if it has 2FA) and the command transparently logs it in first and stores it (still without making it active) — the built-in equivalent of og/cli/docker/entrypoint.sh's shell-level auto-login, useful for containers/CI that always want to act as one specific account:

IXR_USER=ci@example.com IXR_PASSWORD=... ixr whoami --json   # auto-logs in ci@example.com, first time only
IXR_USER=ci@example.com ixr upload-file -f ./report.csv       # every later invocation just uses the stored session

Add IXR_NO_PERSIST (any value) to make that one invocation leave no trace on disk at all: the IXR_PASSWORD auto-login result (and any refreshed token) is kept in memory for this command only, never written to ~/.ixr/credentials. Every invocation re-authenticates from scratch — useful for one-shot/CI runs that shouldn't leave a session file behind:

IXR_USER=ci@example.com IXR_PASSWORD=... IXR_NO_PERSIST=1 ixr upload-file -f ./report.csv

These replace the official CLI's INXT_USER/INXT_PASSWORD/ INXT_TWOFACTORCODE/INXT_OTPTOKEN env vars (which only filled in login's/login-legacy's own flags) — ixr has no equivalent on login itself; use IXR_USER/IXR_PASSWORD for the env-driven auto-login case instead, on any command.

Compatibility with the official Internxt CLI

This is intended to be a mostly drop-in replacement. For ported commands the names, flags, endpoints, payloads and crypto all match, so the two behave the same for everyday login / upload / download / list / move / rename / trash workflows. Credentials are not shared between the two — ixr stores its own session(s) at ~/.ixr/credentials, separate from the official CLI's ~/.internxt-cli, so each needs its own login. Unlike the official CLI, ixr supports several logged-in accounts at once — see Multiple accounts.

The official CLI's commands (built with oclif) are hyphenated (upload-file, move-file, create-folder, …) — ixr uses the exact same primary names. Most of them can also be invoked as separate space-separated words (internxt upload file), and ixr matches that with real nested subcommands, so ixr upload file works the same way. Both forms work on both CLIs, for every command listed above as also having a space form. login-legacy, login-sso and sync-up/sync-down are the exceptions: login-sso and sync-up/sync-down don't exist in the official CLI at all, so only the hyphenated form applies to any of the three.

Known differences:

  • login is an alias, not its own flow. The official CLI's login is SSO-only (no email/password flags at all) and login-legacy is the separate email/password command. ixr's login is an alias for login-sso when built with the default sso feature (same SSO-only behaviour as the official CLI), or for login-legacy when built --no-default-features (which drops sso entirely). login-sso (forces SSO, errors without the sso feature) doesn't exist in the official CLI at all — it's here so you can force a specific flow explicitly instead of relying on which feature login was built with. The SSO flow can't carry the Kyber private key, so hybrid-Kyber workspaces need login-legacy.
  • --json output schema differs. ixr emits a simplified { "success": true, ... } object per command rather than the official CLI's exact JSON envelope. Field names mostly match, but don't assume a byte-identical structure — see each command's JSON output above.
  • No interactive prompting for missing flags, with three exceptions: login-legacy (email/password/2FA), login/login-legacy/login-sso's add-vs-replace prompt when a different account is already active (unless --add/--replace is given — see Multiple accounts), and trash-clear (confirmation, unless --force). Everywhere else, a missing required flag is a clap usage error.
  • Plain-text table output uses simple aligned columns rather than the official CLI's boxed tables. Use --json for stable machine-readable output.
  • serve webdav runs in the foreground, options passed inline, rather than as a pm2-managed background service configured through a separate webdav-config command. The webdav-config / webdav start|stop|status / add-cert daemon-management commands aren't ported — the WebDAV server itself is, as serve webdav.
  • Not yet ported: config, logs, autocomplete.
  • New, with no official equivalent: usage, login-sso, accounts list, accounts switch, download-folder, delete-file/delete-folder (the official CLI has delete permanently file|folder but no plain trash-alias delete file|folder), sync-up, sync-down, id-from-path, path-from-id, the thumbnail command family, mount, and the fuse/smb/nfs/sftp serve backends (the official CLI only serves WebDAV, and only supports one logged-in account). See the command reference above for details on each.

License

MIT.

About

CLI for interacting with Internxt Drive written in rust

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages