MCLI Client Compatibility Notes
mcli is Silo’s build of the MinIO Client (mc). This page records where the two are interchangeable and where they differ.
pgsty/mc forked from the upstream minio/mc at its final commit, 77f82e18 (2025-11-06). The upstream repository was archived in July 2026 without ever cutting a release that contains that commit — so every mcli release is strictly newer than any official mc binary ever published. Documented fork releases: 20260313, 20260321, 20260417, 20260804, 20260806, 20260903 and 20260913. The latest is 20260916; historical notes retain their own comparison baselines.
Current release: RELEASE.2026-09-16T00-00-00Z, package 20260916000000.0.0, source e952aa78f10a. Six OS/architecture archives, RPM/DEB/APK and multi-architecture images are published. See the release notes and component matrix.
Current release changes
20260916 uses pkg v3.14.1, upstream SDK 32e1f32cb176, JWX v3.3.0 and Go 1.27.1.
The SDK retries and reports S3 errors embedded in CopyObject HTTP 200 responses,
so a failed copy cannot authorize mv to remove its source. JWX fixes JSON
field-name escaping; pkg v3.14.0 password-policy semantics are unchanged.
- Per-object
mirrorpermission errors are reported and accumulated while later objects continue; finite jobs exit 1. Failed local destination removals are no longer swallowed. These permission failures do not require--skip-errorsto continue; watch listing/watcher errors retain their existing cancellation/retry behavior. - Failed mirrors suppress normal final success statistics. Explicit
--summarystill reports statistics, with JSONstatus: failure; text errors go to stderr and statistics to stdout. - Failed legalhold set/clear, partial recursive retention failures, and
mvsource-deletion failures now return nonzero. Successful objects are not rolled back. Retention reports each failed object once. - Empty retention durations and invalid find regexes produce normal errors instead of panics. Very short transfers no longer produce infinite speeds in JSON statistics.
Automation migration: affected failure paths that returned 0 now return 1.
Check the final exit code and error records; per-object success start messages
and progress bytes are not completion proof. Configuration, aliases and MC_*
variables are unchanged. See the release notes.
Changes inherited from 20260913
20260913 uses pkg v3.14.0, upstream SDK 60bd07042d49 and Go 1.27.1, with
refreshed Go x/* modules. It preserves mirror destination history and fixes
restart dry runs, noninteractive behavior, transfer/SQL failure exits, explicit
checksums for empty uploads and quiet JSON output. Boolean environment values
accept on/off. Deny/NotResource and bounded wildcard fixes preserve clauses that
could previously be lost; recover already-lost clauses from the original policy.
The password-permission split requires
Console v2.4.1 and a matching Server. Review the existing policy denies as
part of the coordinated upgrade.
Changes inherited from 20260903
The 20260903 client keeps the upstream command, configuration, protocol, and JSON contracts, with these deliberate additions and tightenings since 20260806:
- Read-only checksum audit:
mc checksum verifystreams object data and compares storedFULL_OBJECTCRC32, CRC32C, CRC64NVME, SHA1, or SHA256 values. It supports one object, recursive prefixes, exact or all versions, JSON Lines manifests, dry runs, bounded workers, size/time filters, SSE-C, machine-readable output, private report files, and explicit failure policies. It never repairs or mutates an object. - Credential output is fail-closed:
--debug,admin trace, errors, redirects, trailers, JSON documents, SSE-C paths, custom headers, and registered runtime secrets are scrubbed before display. Support and cluster-export artifacts, including rotated copies, are created with mode0600on POSIX systems. - Correctness repairs: JSON Prometheus metrics no longer panic; empty
pipeinput uses a regular zero-byte PUT; S3 Select responses are no longer closed twice after valid rows are printed; malformed app-level global flags fail instead of being ignored; and service-account policy clearing works again. - Strict writes, permissive reads: named policy and service-account write paths reject empty or malformed policies, bare ARN namespaces, and mixed S3/admin action statements. Existing stored policies remain readable; an empty service-account policy still means “clear the inline policy.”
- Dependency and toolchain floor: builds require Go 1.27.1 and directly consume
github.com/pgsty/silo-pkg/v3v3.13.2.minio-gois pinned to upstream master commit0e78d3f18efe; gRPC is 1.83.2 andx/cryptois 0.56.0. Darwin binaries require macOS 13 or newer. - Verifiable delivery: the public release is immutable, contains 19 checksum-checked assets, and binds archives plus DEB/APK packages to the signed tag and exact source with Sigstore attestations. RPMs carry the PGSTY GPG signature. The release and
latestcontainer tags publish matching linux/amd64 and linux/arm64 manifests.
Two low-level compatibility changes are worth testing in specialized deployments: JWX 3.2 rejects non-compliant JWT crit processing more strictly, and updated Unicode width handling can slightly change table/progress alignment for Indic or ZWJ-heavy strings.
Principles
The fork follows one rule: the shipped artifact and its channels are renamed; the tool you use is not.
- Renamed / replaced — the artifact name on disk (
mcli), the product identity in--versionand--help, the distribution channels (GitHubpgsty/mc, the Pigsty repository,docker.io/pgsty/mc), and the signing keys. Not the command syntax, and — depending on how you install it — not even the name you type. - Preserved interfaces — familiar command syntax, S3/admin protocols and
x-minio-*identifiers; versioned output, authorization and error-handling changes are documented above; the configuration file format and alias semantics;MC_*environment variables (includingMC_HOST_<alias>); the.part.minioresume suffix; and the Go module pathgithub.com/minio/mc. - Severed — every connection to MinIO-operated services: the release/update feed, the SUBNET support and licensing portal, telemetry, and the pre-seeded
playdemo alias. Affected commands remain in the CLI for script compatibility and fail with a stable error rather than disappearing. - Preserved — upstream copyright and the AGPL-3.0 license. Runtime output credits both MinIO, Inc. and PGSTY.
A configuration written by upstream mc is readable by mcli unchanged, and vice versa. SILO releases test mcli against Silo; operation against upstream MinIO and other S3-compatible endpoints is retained on a best-effort basis and should be verified with the exact versions in use.
What changed
Ordered by how likely each change is to affect you, most likely first.
1. The name — what you type, and where the config lives
For many users nothing changes here: the container image keeps mc as its entrypoint, and a binary installed under the name mc behaves identically to upstream. What changed is what we ship — archives and Linux packages install the binary as /usr/local/bin/mcli (package name mcli).
Neither name is hardcoded anywhere. Since 2016 the upstream client has derived its runtime identity from the name it is invoked as, and mcli is the exact rename upstream’s own CONFLICT.md recommended (issue #873) for the Midnight Commander clash — this fork merely promoted that suggestion to the official shipping name, with zero code changes. What that mechanism means in practice:
| Follows the invoked name | Fixed, regardless of the name |
|---|---|
Configuration directory: ~/.mc vs ~/.mcli (Windows: %USERPROFILE%\mc\ vs …\mcli\) |
Environment variables: always MC_* — there is no MCLI_CONFIG_DIR |
| Program name shown in help and usage text | config.json format — identical and interchangeable in both directions |
| Shell-completion registration | All commands, flags, JSON output, exit codes |
User-Agent application suffix (mc/… vs mcli/…) |
--config-dir and MC_CONFIG_DIR overrides |
The one real trap: run mcli for the first time and your existing mc aliases are not there — it starts from an empty ~/.mcli. Either keep invoking it as mc (a symlink suffices — argv[0] is what counts), or copy the state once with cp -a ~/.mc ~/.mcli. For automation and configuration templates, set MC_CONFIG_DIR explicitly: the environment prefix does not follow the name, so one template serves both. Details in Migration.
Get it from GitHub Releases (SHA-256 mcli_<version>_checksums.txt), the Pigsty repository (RPMs GPG-signed, key fingerprint 9592A7BC7A682E7333376E09E7935D8DB9BD8B20), or docker.io/pgsty/mc. Upstream’s minisign key does not sign these artifacts, and dl.min.io is never contacted. Release tags (RELEASE.YYYY-MM-DDTHH-MM-SSZ) and package versions (YYYYMMDDHHMMSS.0.0) keep their upstream schemes.
2. mcli update always fails — on purpose
Self-update is removed. mcli update never contacts the network and never replaces its binary; it prints an explicit notice and always exits 1. Upstream mc update exited 0 when already current, so any cron job or script that calls it and treats a non-zero exit as failure will start failing — drop the call and upgrade through your package manager or GitHub Releases instead. The per-invocation version probe against upstream release feeds is also gone, and MC_UPDATE / MINIO_UPDATE are no longer consulted.
(mcli admin update ALIAS — updating the server — still exists, but Silo servers reject in-place updates server-side.)
3. SUBNET, licensing, and telemetry commands
Everything that reached MinIO SUBNET is disabled at build time. Affected commands keep their names and flags, print a stable notice — “MinIO SUBNET services (registration, licensing, uploads) are disabled in this Silo build of mc; diagnostics remain available locally.” — and exit 1:
| Command | Behavior now | Use instead |
|---|---|---|
mcli license register |
notice, exit 1 |
— |
mcli license update ALIAS (online renewal) |
notice, exit 1 |
mcli license update ALIAS license.key (offline, still works) |
mcli support upload |
notice, exit 1 |
share files through your own channels |
mcli support proxy set |
notice, exit 1 |
proxy remove still clears a legacy setting |
mcli support callhome enable |
notice, exit 1 |
disable / status still work |
The diagnostics themselves stay: mcli support diag / perf / profile / inspect always run in local (airgap) mode — results are written to local files, nothing is uploaded, and SUBNET registration is no longer a prerequisite. Two related hardening changes: inspect no longer falls back to encrypting output with an embedded MinIO public key (your archives stay decryptable by you), and since 20260804 --debug output redacts SUBNET credentials — if you ever shared debug logs from older builds, rotate the keys in them. mcli license info and unregister work locally.
4. The play demo alias is no longer pre-seeded
Fresh configurations seed local, s3, and gcs — not play. Tutorials and smoke scripts that assume the demo alias need it added explicitly: mcli alias set play https://play.min.io <access-key> <secret-key> restores the old behavior, since nothing blocks deliberate access to any S3 endpoint. Existing configuration files are never modified.
5. Output text carries the Silo identity
mcli --version keeps its machine-readable first line and adds an identity line plus dual copyright; --help says “Silo client” and examples use mysilo. Command syntax is untouched — only scripts that grep for upstream identity strings (e.g. “MinIO Client”) need adjusting.
6. For developers
The module path stays github.com/minio/mc, so imports compile unchanged — but go install github.com/minio/mc@latest installs the archived upstream, not this fork. Build from source (git clone https://github.com/pgsty/mc && cd mc && make) or consume it via a replace directive. Contributions need no CLA but require a DCO sign-off (git commit -s). Upstream being archived also means inherited defects are only ever fixed here — most notably minio/mc#5139 (mirror --remove --watch on versioned buckets).
Migration
Moving from an official mc binary to mcli:
- Install
mclifrom one of the fork’s channels (see §1 for verification): GitHub Releases archive,yum install mcli/apt install mclifrom the Pigsty repository, ordocker pull pgsty/mc. - Decide what to call it — this determines which configuration it reads:
- Keep the
mcname (least friction): after confirming no upstream binary remains (command -v mc), install it asmc— e.g.ln -s /usr/local/bin/mcli /usr/local/bin/mc. Invoked asmc, it reads your existing~/.mcuntouched; nothing else to migrate. - Adopt the
mcliname: carry your state over once withcp -a ~/.mc ~/.mcli, or setMC_CONFIG_DIR=~/.mc. Both clients can also coexist side by side, each with its own directory.
- Keep the
- Clean up automation:
- remove
mc updatecalls — they now always exit1; - remove
license register,support upload,support callhome enable, andsupport proxy set— same stable failure; support diag/perf/profile/inspectkeep working and write local files; drop any step that expected a SUBNET upload;- review anything that greps
--versionoutput beyond the first line.
- remove
- Re-check
playusage in tutorials and smoke scripts (§4). - Verify:
mcli --version,mcli alias ls, thenmcli ls <alias>andmcli ping <alias>against your servers. - Rollback stays trivial: the configuration format is identical in both directions, so keeping the old
mcbinary around lets you switch back at any time.
See also
- Silo vs. MinIO — how the
siloserver compares tominio