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.
- Install · Build · Features
- FUSE/WinFSP mount support
- Global flags
- Commands — the full command table
- Command reference — per-command flags and JSON output
- Upload size limit
- Configuration
- Multiple accounts
- Compatibility with the official Internxt CLI
- License
-
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 stillixr), orcargo binstall internxt-clifor a prebuilt binary with no local compile. Plaincargo installonly 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--featuresexplicitly, e.g.cargo install internxt-cli --features webdav-tls,smb,nfs,sftp,termimage,self-update. See Features for the full flag list.self-updateis 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 updateself-updates in place afterwards (standalone-binary installs only — the other methods above manage updates themselves). -
From source: see Build below.
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 webdavSee 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.
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. |
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).
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(envIXR_NONINTERACTIVE) — never prompt for input; error out instead when a required value is missing.--no-timeout(envIXR_NO_TIMEOUTS) — disable the idle-read timeout on network transfers (uploads/downloads). Use if a slow--stdinproducer or--stdoutconsumer 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).
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). |
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": "..." }.
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 accountsJSON output: same shape as login.
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.
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 --allJSON 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": [...] }.
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." }.
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 listJSON output: { "success": true, "accounts": ["a@example.com", "b@example.com"], "active": "a@example.com" }.
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 pickerJSON output: { "success": true, "active": "b@example.com" }.
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
}
}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/ReportsJSON output: { "success": true, "list": { "folders": [...], "files": [...] } }
— always the full (non-extended) item objects, regardless of --extended
(that flag only affects the human-readable table).
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 /DocumentsJSON output: { "success": true, "folder": <DriveFolderData> }.
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 /BackupsA thumbnail is generated automatically for image sources (best-effort, never
fails the upload) — see thumbnail.
JSON output: { "success": true, "file": { "uuid": "..." } }.
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 filesEmpty (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).
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-writeJSON 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.
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 --overwriteJSON output: the sync-down result object
(transferred, deleted, skipped, failed, actions, …).
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> }.
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" } }.
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": "..." } }.
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).
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> }.
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 --forceJSON output: { "success": true, "message": "Trash emptied successfully." }.
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" }.
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 undoJSON output: trashing → { "success": true, "file": { "uuid": "..." } } /
{ "success": true, "folder": { "uuid": "..." } }; with --permanent → the
delete-permanently-* message shape above.
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).
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 --personalJSON output: { "success": true, "workspace": { "id", "name", "bucket", "rootFolderId" } }.
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." }.
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 filessync-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 }]
}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(default127.0.0.1),--webdav-port(default3005),--webdav-https(needswebdav-tlsfeature),--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 onPUT/MKCOL),--webdav-custom-auth--webdav-username/--webdav-password(require HTTP Basic auth).
- FUSE (
--fuse-*):--fuse-mountpoint <DIR>(required whenfuseis served — a directory on Unix, a drive letter likeX:or a directory on Windows),--fuse-allow-other(Unix only — no WinFSP equivalent). - SMB (
--smb-*):--smb-host(default127.0.0.1),--smb-port(default4445— port 445 needs root/admin),--smb-share(defaultinternxt),--smb-username(defaultinternxt),--smb-password(omit for anonymous/guest — most clients, Windows especially, refuse it). - NFS (
--nfs-*):--nfs-host(default127.0.0.1),--nfs-port(default12049— port 2049 needs root/admin). - SFTP (
--sftp-*):--sftp-host(default127.0.0.1),--sftp-port(default2022— port 22 needs root/admin),--sftp-username(defaultinternxt),--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 subfolderWebDAV 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.
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 rootSee 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.
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.pdfJSON output: { "success": true, "uuid": "...", "isFolder": false, "type": "file" }.
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" }.
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.--rawuploads it as-is instead of resizing to a 300x300 PNG. JSON: same shape asgenerate.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(aliasshow, needs thetermimagefeature) — 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 termimageAutomatic thumbnailing (on upload-file, upload-folder, and any serve
backend write) can be disabled everywhere with IXR_THUMBNAILS=0.
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 -yUploads 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):
--no-upload-limit— disable the check entirely.--max-upload-size <SIZE>— a custom cap (5GB,500M,1073741824, … binary units).IXR_MAX_UPLOAD_SIZEenv var — universal override for every upload command. A size string sets a cap;off/none/unlimited/0disables it.- 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.
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.
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--addor--replaceexplicitly 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 sessionAdd 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.csvThese 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.
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:
loginis an alias, not its own flow. The official CLI'sloginis SSO-only (no email/password flags at all) andlogin-legacyis the separate email/password command.ixr'sloginis an alias forlogin-ssowhen built with the defaultssofeature (same SSO-only behaviour as the official CLI), or forlogin-legacywhen built--no-default-features(which dropsssoentirely).login-sso(forces SSO, errors without thessofeature) 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 featureloginwas built with. The SSO flow can't carry the Kyber private key, so hybrid-Kyber workspaces needlogin-legacy.--jsonoutput schema differs.ixremits 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/--replaceis given — see Multiple accounts), andtrash-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
--jsonfor stable machine-readable output. serve webdavruns in the foreground, options passed inline, rather than as a pm2-managed background service configured through a separatewebdav-configcommand. Thewebdav-config/webdav start|stop|status/add-certdaemon-management commands aren't ported — the WebDAV server itself is, asserve 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 hasdelete permanently file|folderbut no plain trash-aliasdelete file|folder),sync-up,sync-down,id-from-path,path-from-id, thethumbnailcommand family,mount, and thefuse/smb/nfs/sftpservebackends (the official CLI only serves WebDAV, and only supports one logged-in account). See the command reference above for details on each.
MIT.