This is the multi-page printable view of this section. .
Release Notes
- 1: SILO 20261026: Server, mcli and KES
- 2: SILO 20261018 Release Notes
- 3: silo-pkg 3.14.3 Released
- 4: PGSTY KES 20261018 Released
- 5: SILO Console 2.4.2 Released
- 6: mcli 20261018 Released
- 7: silo-pkg 3.14.2 Released
- 8: SILO 20261015: Superseded Draft Record
- 9: SILO Console 2.4.1 Released
- 10: silo-pkg 3.14.1 Released
- 11: SILO 20260916 Release Notes
- 12: mcli 20260916 Released
- 13: mcli 20260913 Released
- 14: silo-pkg 3.14.0 Released
- 15: SILO Console 2.4.0 Released
- 16: Silo Console 2.2.0 Release Notes
- 17: SILO Console 2.5: Interface and Administration Updates
- 18: silo-pkg 3.15: Go 1.27.3 and S3 SDK Fixes
- 19: Silo Console 2.1.0 Released
- 20: Silo Console 2.0.0 Released
- 21: silo-pkg 3.13.0 Released
- 22: silo-pkg 3.12.0 Released
- 23: Silo Pkg 3.11.0 Released
- 24: Silo 20260903 Released
- 25: Explicit Version Deletes Now Require DeleteObjectVersion
- 26: mcli 20260903 Released
- 27: mcli 20260806 Released
- 28: Silo 20260806 Released
- 29: Silo 20260804 Released
- 30: mcli 20260804 Released
- 31: Silo 20260618 Released
- 32: Silo 20260417 Released
- 33: Silo 20260325 Released
- 34: Silo 20260321 Released
- 35: Silo 20260314 Released
- 36: Silo 20260214 Released
- 37: Silo 20251203 Released
Release notes track feature updates, security fixes, dependency upgrades, compatibility requirements, and artifact delivery across the SILO stack.
The 20261026 coordinated release consolidates all SILO Server, mcli, and KES changes since 20261018 alongside Console 2.5.1 and silo-pkg 3.15.2 (the October 24–25 maintenance drafts have been folded into the final release notes, with their URLs redirecting automatically).
1 - SILO 20261026: Server, mcli and KES
SILO, mcli, and PGSTY KES 20261026 are now released, alongside SILO Console 2.5.1 and silo-pkg 3.15.2. This article consolidates the October 24–25 maintenance records and summarizes all changes since the 20261018 release. All three executables share the same release date and package version; KES remains an optional, independently deployed key encryption service.
Released components
| Component | Version | Source | Delivery |
|---|---|---|---|
| SILO Server | 20261026000000.0.0 |
c3ad836aed0d |
Platform archives, Linux packages, Docker Hub |
| mcli | 20261026000000.0.0 |
0ceb298cdcd0 |
Platform archives, Linux packages, Docker Hub |
| PGSTY KES | 20261026000000.0.0 |
e9b4f1acebc1 |
Platform archives, Linux packages, Docker Hub |
| SILO Console | 2.5.1 |
fff1618d9abb |
Embedded in Server; standalone binaries, packages, and image |
| silo-pkg | 3.15.2 |
0416eb1f4505 |
Go source module |
All five projects build with Go 1.27.3. Server, Console, and mcli select silo-pkg v3.15.2 and the pinned upstream commit minio-go v7.4.2-0.20261025165926-320af050c445 (a pseudo-version rather than an upstream v7.4.2 tag). Server explicitly embeds the released Console and mcli sources; upgrading standalone Console does not replace the UI embedded inside an older Server binary. See the component matrix and downloads.
Server changes since 20261018
- LDAP and IAM (#250): Restores group-based login for LDAP users whose normalized DNs exceed 255 bytes in a single ASCII path component by treating missing optional IAM records on those paths as absent, while continuing to fail closed on unreadable revocation records or storage errors (writing overlong path records remains unsupported on the object backend). Policy reloads no longer hold the authentication-cache lock during storage I/O, swap the cache only after a complete load, and preserve IAM write ordering. Request cancellation now propagates to KMS and SSE-S3 decryption paths without shortening the caller’s policy-query deadline.
- Built-in policy Reset and live bucket inventory: Resetting an overridden built-in policy immediately restores the native policy definition on the next read, while delayed peer/etcd notifications reload committed state without clobbering newer edits (in-use checks, delete revisions, and custom-policy deletion remain unchanged). Console’s
account-infoendpoint now reads the live bucket inventory so newly created or deleted buckets appear immediately on refresh, while object usage totals remain scanner-derived. - Multi-pool deletion safety (#257, #256): Ordinary and bulk deletes clean non-authoritative duplicate pools before removing the authoritative copy, select an available pool when writing new delete markers, and protect remote-tier payloads still referenced by surviving copies.
- Versioned lifecycle expiration (#253, #252): Retains remote tier payloads whenever lifecycle expiration only inserts a delete marker (including non-
nullversions in versioning-suspended buckets), reclaiming remote tier objects only when a historical version is permanently purged and no surviving reference needs its payload. - Multipart replication (#255, #254): Forwards the
FULL_OBJECTchecksum type when replicating CRC multipart objects so the target cluster does not reject the replica withBadDigest. - Storage RPC and timeout semantics: Forwards storage RPC deletion options (including the previous data directory needed for overwrite rollback) while preserving existing drive/RPC deadlines, request cancellation, object locking, read quorum, and healing rules. The final release withdrew the 20261025 draft’s admitted-I/O draining waits and per-object mutation guards (including #258) in
dcc2f20ba; see the multi-pool object consistency record. - Dependencies, packaging, and delivery: Updates SFTP, etcd, NATS, cloud SDKs, and other Go modules; preserves Kafka’s default protocol
2.1, CLI formatting, and NetBSD compatibility; and fixes legacy DARE v1 unsealing while keeping v2 writes. Container images, the verified installer, and Helm client jobs bundle mcli 20261026. Windows archives now ship as.zip, staticcurlis updated to8.23.0with the certificate-trust mitigation described below, and RPM signer verification is fixed during the final SBOM/provenance refresh. - Documentation and migration: Incorporates the single-node MinIO migration guide (#228), reconciles operational defaults, and consolidates upgrade instructions under the migration guide.
This release retains all Object Lock, IAM authorization, durable STS revocation, pool-migration, and LIST recovery repairs shipped in 20261018. The dependency security inventory below separates newly adopted fixes from earlier repairs and scanner findings in unused code paths.
mcli changes since 20261018
- Client identity cache isolation (#50, #55): Isolates cached S3 and admin clients by credentials, TLS trust, proxy, custom resolver, and request headers while preserving credential refresh and token-file rotation.
- Replication resync status and throughput (#48, #49, #52):
admin replicate resync status --jsondrains the status stream through terminalCompleted,Failed, orCanceledstates so completed jobs emit their final JSON record; explicitly reports when replication is disabled; stops waiting onFailedorCanceledin TTY mode; and fixes integer overflow in throughput calculations past ~8.6 GiB. - Honest exit codes (#51):
resync startandresync cancelretain per-bucket error details and return a nonzero exit code when any bucket fails, whileresync statuscontinues to exit0when successfully inspecting an alreadyFailedorCanceledjob.admin info --jsonalso exits nonzero on request failure while preserving its JSON error output. - Batch job observation (#53, #54):
batch statuscontinues watching when live metrics are not yet available, emits apendingstatus instead of treating queued or missing samples as complete, falls back to bounded persisted-status queries when the metric stream ends early, and fixes IOPs integer overflow on large object counts. - Alias regions (#44): Honors persisted alias
regionsettings with precedenceMC_REGION→AWS_REGION→ alias configuration → bucket-location discovery, and isolates signing regions across cached clients (without adding analias set --regionCLI flag). - SDK fixes: Rejects truncated sequential or parallel multipart streams; preserves
ReadAtEOF/negative-offset semantics and post-Statread/seek bounds; deduplicates fallback batch-delete errors; and accepts both HTTP200and204when removing bucket replication configurations (while still reporting403and other real errors). - Packaging and help (#45): Builds with Go 1.27.3 and GoReleaser 2.19.0, switches Windows archives to
.zip, refreshes help URLs and disabled server-update/SUBNET messages, and preserves existing CLI configuration andMC_*environment variables.
Automation scripts should handle the pending batch state and nonzero exit codes on partially failed resync start/cancel commands. Upgrading mcli requires no Server protocol change. Mid-stream download interruptions still fail immediately; SIGINT during a multipart upload exits 130, and incomplete upload parts may require explicit cleanup. See the complete client commit history.
KES changes since 20261018
KES 20261026 rolls up the Go 1.27.3 toolchain and cloud-SDK dependency maintenance and aligns the executable, Linux packages, and container images with the 20261026 release batch. Its HTTP API, configuration schema, and stored-key format remain unchanged.
| Dependency | 20261018 | 20261026 |
|---|---|---|
| Go | 1.27.2 | 1.27.3 |
| AWS SDK v2 core | 1.47.3 | 1.47.4 |
| AWS config / credentials | 1.33.9 / 1.20.9 | 1.33.10 / 1.20.10 |
| AWS Secrets Manager | 1.50.4 | 1.50.6 |
| Google Secret Manager | 1.22.0 | 1.23.0 |
| Google API / IAM | 0.304.0 / 1.14.0 | 0.308.0 / 1.15.0 |
| Google certificate proxy / GAX | 0.3.23 / 2.26.2 | 0.3.24 / 2.27.0 |
| go-rootcerts / procfs | 1.0.2 / 0.22.0 | 1.1.0 / 0.23.0 |
The 25 module updates also cover AWS support packages and Google generated APIs, while keeping Lip Gloss v1 and gopkg.in/yaml.v3. Packaging uses GoReleaser 2.19.0 and nFPM 2.48.0; Linux packages install /usr/local/bin/kes without auto-starting an unconfigured service.
Compatibility checks verified OpenBao KV v2 and local Moto Secrets Manager fixtures across pre-upgrade and post-upgrade encrypted objects, version rollbacks, and cold Server restarts. Preserve persistent keys, certificates, and configuration across upgrades, and verify object decryption after restarting Server. See the complete KES commit history.
Console 2.5 and silo-pkg 3.15
Console 2.5.1 delivers the 2.5 interface refresh, permission-aware navigation, Overview dashboard, bounded object previews, Tasks center, identity management, and high-latency diagnostic stream fixes; the 2.5.1 patch additionally preserves group members during status changes, OIDC string-array group permissions, secure session cookies behind TLS proxies, exact UTF-8 credential exports, streaming ZIP error statuses, and keyboard focus/accessibility labels. Server 20261026 embeds Console 2.5.1 directly.
silo-pkg 3.15.2 combines the 3.15.0 Go 1.27.3 and dependency refresh, 3.15.1 upstream SDK fixes, and 3.15.2 HTTP 204 handling when removing bucket replication, while retaining existing policy semantics and the 3.14.3 admin-action lookup optimization. As a Go source module, silo-pkg is adopted by rebuilding dependent applications.
Upgrade and failure boundaries
- Coordinated upgrade: Back up IAM state, configuration, and encryption keys, and upgrade all Server nodes and replication peers together following the IAM upgrade guide. Running mixed old/new Server versions against a shared IAM backend or performing rolling downgrades remains unsupported.
- Post-upgrade checks: Verify LDAP/OIDC login, built-in policy Reset, bucket list refresh, versioned reads, cross-site replication, and encrypted-object decryption. The
FULL_OBJECTmultipart replication fix requires upgrading the sending Server first and then checking its pending replication queue. - Archive formats and scripts: Windows archives now use
.zip(Linux and macOS retain.tar.gz); update automation scripts formcli batch statuspendingoutputs and nonzero exit codes on partially failedresync start/cancelcommands. Existing environment variables, configuration filenames, and on-disk formats remain unchanged. - Known multipart and issue scope: Public
ListMultipartUploadscontinues to use the node-local in-memory cache and exact-key lookup, ignoring the retiredstrictswitch with a startup warning. Issues #79 and #217 remain open, webhook hot-reload PR #249 is not included, and broader performance symptoms remain tracked in #251.
Storage timeout and consistency boundary: A client-visible request timeout does not guarantee that underlying disk or RPC I/O has stopped or that the write failed to commit. Late commits, delayed rollbacks, and stale values or tags remain possible after partial drive failures, lock lease expiration, or network partitions. This release does not add distributed lock fencing and cannot automatically recover previously lost objects, remote tier payloads, or metadata.
Downloads and verification
See the download matrix for platform archive filenames, SHA-256 checksums, Linux packages, and verification commands. Versioned container images are available on Docker Hub:
- Server standard image (Linux
amd64/arm64manifest):sha256:7bb2aff6b89efe1cb1303c10fc21d579447434ed06649ac9ec05a5e2dbf30ad7(matcheslatest) - Server distroless image (Linux
amd64/arm64manifest):sha256:e6544a902f426f2b4e775b89f3c1cd78310dbffc262d1ed82e9800c1cb1b5885(matchesdistroless, with nativesilo healthcheck readyconfigured on both architectures; see the Server image workflow)
Each component’s artifacts are identified by its release commit, archive checksums, package signatures, and build provenance:
- Server artifact verification: Verified 36 release assets and 34 attested subjects (including six platform binaries and six Linux packages) built from
c3ad836aed0d. Both RPM packages are GPG-signed, and the checksum manifests, SBOMs, and provenance attestations match the final packages (see the Go CI, VulnCheck, Release, and Finalize workflows). - Local multi-node and fault-injection verification: Passed 88 baseline four-node, 88 upgraded four-node, and 136 eight-node multi-pool S3 checks on Linux
arm64, preserving historical objects, tags, encrypted objects, and in-flight multipart uploads across the coordinated upgrade. Rolling restarts,SIGKILL, and a 75-second pause/recovery test passed with all 408 acknowledged writes read back with matching hashes and complete listings; the native trusted-TLSmclisuite and 16 persistence checks also passed. Because health endpoints evaluate reported drive quorum rather than per-node installed peer connections, run a data-path check before resuming traffic after a restart (see Startup Readiness Window). - KES artifact and integration verification: Verified all 19 public assets, 11 attested subjects, and both signed RPMs. OpenBao KV v2 and Moto fixtures exercised the Darwin
arm64KES binary across upgrade, rollback, and cold Server restarts—first against SILO 20261018 and then against the final Server/mcli 20261026 Linuxarm64binaries. Both KES Linux container images match the archive binaries, with multi-architecture manifest digestsha256:ea36fcdd260c606eb89e693b0949aea73b149359605273a98fcf13c8433049cd(Release workflow · Image workflow).
Contributors and change history
Thanks to Denis Babchuk / @botdeniska for lifecycle, replication, and multi-pool deletion repairs; @910patin for long-DN LDAP login; Azat / @azataiot for reproducing high-latency health-report truncation; @MikeVL for reporting resync throughput overflow and JSON status output gaps; @mrjavadseydi for alias-region support and the webhook reload proposal; Gerald Combs / @geraldcombs for Windows archive feedback; @yupoet for the single-node migration guide; and @GhaziTriki, @devTeam-AiReg, and @sargarass for their bug reports and feedback.
The table below summarizes the community Issues and PRs merged, adopted, or tracked across the five component repositories during this release cycle:
| Project | Contributor | Issue / PR record | Summary |
|---|---|---|---|
| SILO | @botdeniska | #252, #253; #254, #255; #256, #257; #245, #248 | Fixed tiered-object lifecycle expiry, multipart FULL_OBJECT replication checksums, and multi-pool deletion safety; the cross-pool migration repair in #248 was adopted in 60c7179dd with co-authorship retained. |
| SILO | @910patin | #250 | Fixed long-DN LDAP login failures. |
| SILO | @yupoet | #228 | Contributed the field-validated single-node migration guide. |
| SILO | @mrjavadseydi | #249 | Webhook hot-reload proposal (under review, not included in this release). |
| SILO | @sargarass | #79 | Multipart upload listing feedback (tracked for a future release). |
| SILO | @GhaziTriki | #251 | Performance report (under investigation). |
| SILO | @devTeam-AiReg | #247 | Release availability feedback. |
| SILO | @Vonng | #217, #258 | Multi-site replication boundary notes (#217) and candidate lock-isolation experiment (#258, reverted before final release). |
| mcli | @mrjavadseydi | #44 | Added persistent custom S3 region support in alias configurations. |
| mcli | @MikeVL | #48, #49 | Reported replication resync throughput overflow and missing --json status output (fixed in this release). |
| mcli | @geraldcombs | #45 | Proposed .zip packaging for Windows release archives (adopted). |
| mcli | @Vonng | #50–#55 | Client identity-switch isolation and replication resync status repairs. |
| Console | @azataiot | #63 | Reported and reproduced high-latency health-report truncation (fixed in Console 2.5.1). |
| Console | @Vonng | #64–#69 | Dependency updates, UI improvements, end-to-end verification, and release maintenance for Console 2.5.1. |
| silo-pkg | @botdeniska | #11 | Policy evaluation fast-path optimizations. |
| KES | @Vonng | 20261018–20261026 release maintenance | Dependency refresh, packaging, and OpenBao / Moto compatibility verification. |
The community contributor roll recognizes every human issue and PR author across all repositories. Full commit comparisons: Server, mcli, KES, Console, and pkg.
The sections below summarize the October 22–25 dependency and security audit records (dependencies shared across multiple components are listed once under the corresponding package):
Additional Go dependency updates on October 24
The Go toolchain remains 1.27.3. Compared with the October 22 candidate dependencies, this update includes 26 newly released third-party module versions, plus Arrow and codescan updates (28 modules in total):
| Dependency | Version change | Components |
|---|---|---|
| pkg/sftp | 1.13.11 → 1.13.12 | SILO |
| etcd api/v3, client/pkg/v3, client/v3 | 3.7.2 → 3.7.3 | silo-pkg, mcli, Console, SILO |
| JWX | 3.3.0 → 3.4.0 | silo-pkg, mcli, Console, SILO |
| nats.go | 1.54.0 → 1.55.0 | SILO |
| procfs | 0.22.0 → 0.23.0 | mcli, Console, SILO, KES |
| AWS SDK v2, 14 modules | core 1.47.3 → 1.47.4; config 1.33.9 → 1.33.10; credentials 1.20.9 → 1.20.10; Secrets Manager 1.50.5 → 1.50.6, with matching supporting modules | KES |
| GCP IAM / Monitoring / Secret Manager / Google API | IAM 1.14.0 → 1.15.0; Monitoring 1.31.0 → 1.32.0; Secret Manager 1.22.0 → 1.23.0; Google API 0.307.0 → 0.308.0 | IAM and Google API: SILO, KES; Monitoring: SILO; Secret Manager: KES |
| go-rootcerts | 1.0.2 → 1.1.0 | KES |
| Arrow | 18.7.0 → 18.8.0 | SILO, through the Azure SDK |
| codescan | 0.36.4 → 0.36.6 | Console generator toolchain |
Public API behavior and output formats remain unchanged; see the component table above for the final pinned module versions.
Included runtime security fixes
Go and certificate trust
CVE-2026-111943 / GO-2026-6809: Go 1.27.3 rejects known untrustworthy intermediate certificates in crypto/x509, protecting TLS verification even when host trust stores have not yet been updated. All five primary projects and the standalone debugging modules are built with Go 1.27.3.
In addition, the UBI container trust stores for SILO, mcli, and KES apply the root certificate blocklist from Red Hat RHSB-2026-012. Because blocking a root rejects every certificate chain anchored under that root, this container-level mitigation is broader than Go 1.27.3’s intermediate-certificate check; external services still using certificates chained to those roots must rotate their certificate chains. See Locally mitigated items below for the UBI CA package status.
SFTP dependency hardening
pkg/sftp 1.13.12 fixes three security issues disclosed on October 23. At the time of this review, none has a CVE identifier. They are recorded separately by GHSA and are not counted as new CVEs:
| Advisory | Upstream issue | Applicability to SILO |
|---|---|---|
| GHSA-xgcg-wgmj-5rw3, Medium | Client.RemoveAll follows a top-level symlink and can delete contents of the target directory |
The SILO server does not call this client API |
| GHSA-47hq-m6q9-3rm6, Medium | ReadOnly mode on sftp.Server permits hardlink creation |
SILO uses NewRequestServer and its own driver, not this read-only Server API |
| GHSA-h959-g3h6-w76c, High | ReadOnly mode permits file truncation or creation | Same as above |
Because the latter two upstream advisories have inconsistent affected-range and patched-version metadata, this batch upgrades directly to 1.13.12 containing the code fixes and verifies them with regression tests (the Go vulnerability database snapshot remains at October 21 and has not yet ingested these October 23 advisories).
Static curl and linked libraries
The static curl binary bundled in SILO container images is upgraded from 8.22.0 to 8.23.0 (built with HTTP/2, SSH, and Brotli enabled over the OpenSSL backend). The table below lists all 21 CVEs from the official curl 8.22.0 vulnerability summary alongside their applicability to SILO’s static build:
| CVE | Issue / applicability |
|---|---|
| CVE-2026-92392 | HTTP/2 upload use-after-free |
| CVE-2026-107929, CVE-2026-94266, CVE-2026-94265, CVE-2026-86413, CVE-2026-86414, CVE-2026-86415, CVE-2026-87861 | Connection reuse and credential/authentication boundaries |
| CVE-2026-92386, CVE-2026-86412, CVE-2026-87862, CVE-2026-87863 | Cookie and HSTS state handling |
| CVE-2026-92387, CVE-2026-92388, CVE-2026-92389, CVE-2026-92390, CVE-2026-92391 | Loops, FTP/TFTP, download filenames, and MIME recursion |
| CVE-2026-86410, CVE-2026-87865 | SSH host verification and known_hosts callbacks |
| CVE-2026-107928, CVE-2026-87866 | GnuTLS / wolfSSL only; neither backend is used by SILO’s OpenSSL build |
The statically linked Alpine libraries are also updated to the following patched revisions (-rN denotes the Alpine security backport revision):
| Library | Version change | CVE |
|---|---|---|
| Brotli | 1.2.0-r1 → 1.2.1-r0 | CVE-2026-111497 |
| musl | 1.2.6-r2 → 1.2.6-r3 | CVE-2026-107891 |
| libssh2 | 1.11.1-r3 → 1.11.1-r4 | CVE-2025-15661, CVE-2026-58050, CVE-2026-58051, CVE-2026-66032, CVE-2026-66033, CVE-2026-66034, CVE-2026-66035 |
The previous SILO arm64 image already linked OpenSSL 3.5.9-r0, nghttp2 1.70.0-r0 (CVE-2026-58055), and zlib 1.3.2-r1 (CVE-2026-85091); this update enforces minimum version floors in the build script and verifies both architectures against the official Alpine security metadata.
Build and development security fixes
This section covers the Node.js, Vite, and code-generation toolchain used during SILO Console frontend compilation and testing (Console runs in production as a compiled Go server binary), as well as the standalone Go debugging modules in the server repository.
Node 24.22.1
The build environment updates Node.js from 24.22.0 to 24.22.1, addressing all eight Node 24.x CVEs in the October 2026 Node.js security release (excluding Node-26-only findings):
| CVE | Fix |
|---|---|
| CVE-2026-105482 | DNS names truncated at NUL |
| CVE-2026-105483, CVE-2026-106069, CVE-2026-106072 | TLS hostname checks for non-canonical IP addresses |
| CVE-2026-111497 | Brotli decoder out-of-bounds write (backported in Node while the embedded version string remains 1.2.0) |
| CVE-2026-105481 | HTTP keep-alive accepts data sent before a request |
| CVE-2026-106074 | Random caches reused across startup snapshots |
| CVE-2026-106073 | Permission-model bypass in fs.realpathSync |
Node 24.22.1 updates bundled Undici from 7.29.1 to 7.30.1, resolving eight CVEs documented in the Undici v7.30.1 release notes:
| CVE | Fix |
|---|---|
| CVE-2026-87018, CVE-2026-109383 | TLS renegotiation crash and lost Agent verification callbacks |
| CVE-2026-109829, CVE-2026-109841 | Cross-origin cache disclosure and SRI bypass |
| CVE-2026-109869, CVE-2026-87029, CVE-2026-109924, CVE-2026-109932 | Denial of service in content decoding, EventSource, and WebSocket handling |
Node 24.22.1 also updates bundled OpenSSL from 3.5.8 to 3.5.9, incorporating 13 fixes across DTLS, QUIC, CRLs, CMP, EC/SM2, and handshake memory safety: CVE-2026-84782, CVE-2026-35189, CVE-2026-35191, CVE-2026-42772, CVE-2026-54872, CVE-2026-54873, CVE-2026-54875, CVE-2026-72897, CVE-2026-75804, CVE-2026-75805, CVE-2026-75806, CVE-2026-77696, CVE-2026-84784.
Node 24.22.1 updates bundled c-ares from 1.34.8 to 1.34.9, resolving 11 upstream GHSAs that do not carry separate CVE IDs (GHSA-wpr3-8q3r-4q8c covers a connection-shutdown path omitted from the earlier CVE-2026-33630 fix and remains listed as No known CVE):
| Advisory | Fix |
|---|---|
| GHSA-wpr3-8q3r-4q8c | Memory corruption when cancelling after connection failure |
| GHSA-fqh5-3x7m-wp44 | Replies accepted from the wrong DNS server |
| GHSA-97vq-4mxv-7vhm | Freed query used after failed connection shutdown |
| GHSA-rf6g-ww74-97qh | Use-after-free during cancellation/shutdown callbacks |
| GHSA-mw23-pfg6-2x9r | Use-after-free after socket-state callbacks |
| GHSA-v33j-65h4-4ch8 | Use-after-free when server-state callbacks cancel queries |
| GHSA-pj7p-v3cw-hq66 | Freed server record used after a callback changes servers |
| GHSA-grv7-fq5q-9vfc | Cached answer freed while being read |
| GHSA-ph6c-3cvx-mq6v | Buffer overflow during concurrent option copying/server updates |
| GHSA-cfcv-8v2j-qj6r | Uninitialized memory in IPv6 addresses with prefix lengths |
| GHSA-wrgc-prmw-27xh | Cache reuses replies obtained with different EDNS options |
Frontend dependencies and code generation
| Dependency | CVE / GHSA | Fix |
|---|---|---|
| fast-uri 3.1.7 → 3.1.9 | CVE-2026-86472 | Inconsistent normalization of encoded hostnames |
| ip-address 10.4.0 → 10.7.4 | CVE-2026-101913 | Incorrect link-local IPv6 classification |
| ip-address 10.4.0 → 10.7.4 | CVE-2026-101910 | Unrecognized local-use NAT64 range |
| ip-address 10.4.0 → 10.7.4 | CVE-2026-101912 | Subnet checks mix address families |
| ip-address 10.4.0 → 10.7.4 | CVE-2026-101911 | Unbounded diagnostics for long addresses |
| moment 2.30.1 → 2.31.0 | CVE-2026-17495 | Path traversal through non-string locale input |
| smol-toml 1.8.0 → 1.9.1 | GHSA-r4xh-jqrq-34v2 | Quadratic TOML parsing |
| vite 8.0.16 → 8.3.5 | CVE-2026-112945 | Cross-origin script execution in development middleware |
| vite 8.0.16 → 8.3.5 | CVE-2026-112946 | Development-server filesystem allowlist bypass |
| vite 8.0.16 → 8.3.5 | CVE-2026-112943 | Insufficient Windows editor-argument escaping |
| vite 8.0.16 → 8.3.5 | CVE-2026-112944 | Editor arguments interpreted as command options |
| vite 8.0.16 → 8.3.5 | CVE-2026-112942 | Windows editor-path check bypass |
In addition, go-swagger is updated from 0.33.2 to 0.36.6, resolving GHSA-8gmg-wf5j-hvf7 and GHSA-rg22-m539-jwvf (code injection during code generation when processing untrusted OpenAPI content types or enum values; first fixed upstream in 0.35.3 without a separate CVE ID). Generated files are synchronized with the updated tool. The generation process explicitly preserves the reviewed configuration-import binder and multipart consumer, retaining the existing file-size, filename and form-encoding behavior under regression tests.
Independent Go debugging modules
The standalone inspect, s3-verify, and xattr module graphs in the server repository previously lagged behind the root module. Updating their dependencies clears 37 CVE-numbered dependency findings and one non-CVE S2 advisory:
| Dependency | Affected independent modules | Selected version |
|---|---|---|
| golang.org/x/crypto | inspect, s3-verify | 0.58.0 |
| golang.org/x/net | s3-verify | 0.61.0 |
| golang.org/x/text | s3-verify | 0.43.0 |
| golang.org/x/sys | inspect, xattr | 0.49.0 |
| klauspost/compress | inspect, s3-verify | 1.20.3 |
The table below links each finding to the Go vulnerability database and records the first upstream fixed version (the unfixed OpenPGP notice is listed separately under Build-stage and non-CVE advisories):
| CVE / advisory | Dependency | First fixed | Issue |
|---|---|---|---|
| GO-2026-5841 | klauspost/compress | v1.18.7 | OOB read in github.com/klauspost/compress/s2 |
| CVE-2026-39824 | x/sys | v0.44.0 | integer overflow in NewNTUnicodeString in golang.org/x/sys/windows |
| CVE-2025-47913 | x/crypto | v0.43.0 | Potential denial of service in golang.org/x/crypto/ssh/agent |
| CVE-2025-58181 | x/crypto | v0.45.0 | Unbounded memory consumption in golang.org/x/crypto/ssh |
| CVE-2025-47914 | x/crypto | v0.45.0 | Malformed constraint may cause denial of service in golang.org/x/crypto/ssh/agent |
| CVE-2026-39833 | x/crypto | v0.52.0 | key constraints not enforced in golang.org/x/crypto/ssh/agent |
| CVE-2026-39832 | x/crypto | v0.52.0 | agent constraints dropped when forwarding keys in golang.org/x/crypto/ssh/agent |
| CVE-2026-46597 | x/crypto | v0.52.0 | byte arithmetic causes underflow and panic in golang.org/x/crypto/ssh |
| CVE-2026-39828 | x/crypto | v0.52.0 | bypass of certificate restrictions in golang.org/x/crypto/ssh |
| CVE-2026-39835 | x/crypto | v0.52.0 | server panic during CheckHostKey/Authenticate in golang.org/x/crypto/ssh |
| CVE-2026-39827 | x/crypto | v0.52.0 | memory leak when rejecting channels can lead to DoS in golang.org/x/crypto/ssh |
| CVE-2026-39830 | x/crypto | v0.52.0 | client can cause server deadlock on unexpected responses in golang.org/x/crypto/ssh |
| CVE-2026-39829 | x/crypto | v0.52.0 | pathological RSA/DSA parameters may cause DoS in golang.org/x/crypto/ssh |
| CVE-2026-39831 | x/crypto | v0.52.0 | bypass of FIDO/U2F security keys physical interaction in golang.org/x/crypto/ssh |
| CVE-2026-39834 | x/crypto | v0.52.0 | infinite loop on large channel writes in golang.org/x/crypto/ssh |
| CVE-2026-42508 | x/crypto | v0.52.0 | auth bypass via unenforced @revoked status in golang.org/x/crypto/ssh/knownhosts |
| CVE-2026-46595 | x/crypto | v0.52.0 | VerifiedPublicKeyCallback permissions skip enforcement in golang.org/x/crypto/ssh |
| CVE-2026-46598 | x/crypto | v0.52.0 | pathological inputs can lead to client panic in golang.org/x/crypto/ssh/agent |
| CVE-2026-56854 | x/crypto | v0.55.0 | Source-address critical option not enforced for non-public-key auth callbacks in golang.org/x/crypto/ssh |
| CVE-2026-78662 | x/crypto | v0.56.0 | Prevent DoS on deadlocked undecided channel in golang.org/x/crypto/ssh |
| CVE-2026-56855 | x/crypto | v0.56.0 | Prevent DoS on deadlocked established channel in golang.org/x/crypto/ssh |
| CVE-2025-47911 | x/net | v0.45.0 | Quadratic parsing complexity in golang.org/x/net/html |
| CVE-2025-58190 | x/net | v0.45.0 | Infinite parsing loop in golang.org/x/net |
| CVE-2026-33814 | x/net | v0.53.0 | Infinite loop in HTTP/2 transport when given bad SETTINGS_MAX_FRAME_SIZE in net/http/internal/http2 in golang.org/x/net |
| CVE-2026-42506 | x/net | v0.55.0 | incorrect handling of namespaced elements in foreign content in golang.org/x/net/html |
| CVE-2026-39821 | x/net | v0.55.0 | failure to reject ASCII-only Punycode-encoded labels in golang.org/x/net/idna |
| CVE-2026-42502 | x/net | v0.55.0 | incorrect handling of HTML elements in foreign content in golang.org/x/net/html |
| CVE-2026-25680 | x/net | v0.55.0 | denial of service when parsing arbitrary HTML in golang.org/x/net/html |
| CVE-2026-25681 | x/net | v0.55.0 | incorrect handling of character references in DOCTYPE nodes in golang.org/x/net/html |
| CVE-2026-27136 | x/net | v0.55.0 | duplicate attributes can cause XSS in golang.org/x/net/html |
| CVE-2026-46600 | x/net | v0.56.0 | Parsing an invalid SVCB or HTTPS RR can panic in golang.org/x/net/dns/dnsmessage |
| CVE-2026-78659 | x/net | v0.60.0 | HTTP/2 server memory exhaustion due to Trailer headers in net/http |
| CVE-2026-78660 | x/net | v0.60.0 | HTTP/2 transport accepts malformed framing-related headers in net/http |
| CVE-2026-78669 | x/net | v0.60.0 | Excessive CPU consumption from repeated initial window changes in net/http |
| CVE-2026-78663 | x/net | v0.60.0 | Double flow control refund on HTTP/2 server streams in net/http |
| CVE-2026-97032 | x/net | v0.60.0 | HTTP/2 server crash due to HPACK encoder race in net/http |
| CVE-2026-56852 | x/text | v0.39.0 | Infinite loop on invalid input in golang.org/x/text |
| CVE-2026-56851 | x/text | v0.41.0 | Panic parsing crafted input in x/text/secure/precis in golang.org/x/text |
Outstanding upstream or distribution issues
As of 2026-10-23, the advisories below do not yet have an upstream or distribution release available. Rather than suppressing scanner rules, we record each finding alongside its local mitigation or runtime reachability.
Locally mitigated, awaiting upstream publication
| Issue | Upstream / distribution status | Action and remaining boundary |
|---|---|---|
| CVE-2026-93687 / braces | Latest npm version remains 3.0.3, with no upstream fixed release | A local Yarn patch limits parsing and AST traversal in [email protected] to depth 100 (13 regression tests pass). The package is only in the frontend build/test graph; version-only scanners continue to report 3.0.3 until an upstream release replaces the patch. |
| CVE-2026-111943 / UBI CA | At acceptance, UBI 9 supplied ca-certificates 2025.2.80_v9.0.305-91.el9 without the updated trust list |
The Red Hat root blocklist is applied during image build and verified on the output PEM bundle; Go 1.27.3 and Node 24.22.1 also enforce their own built-in certificate checks. |
UBI 9.8 findings without an applicable vendor fix
Scans conducted on 2026-10-22 against the SILO, mcli, and KES system-component acceptance images (containing the updated static curl and CA bundle on amd64 and arm64) each report 24 RPM findings across the same 12 unique CVEs (15 Medium and 9 Low per image, zero High/Critical, and no vendor FixedVersion). Red Hat security queries on 2026-10-23 continue to list the RHEL 9 source packages below as Affected or Fix deferred.
Because RPM scanners map advisories by source package (SRPM), defects in uninstalled CLI utilities or other CPU architectures are reported against shared runtime packages such as libgcc and glibc. The applicability of each entry is detailed below:
| CVE | Source package | Red Hat severity | RHEL 9 status | Applicability |
|---|---|---|---|---|
| CVE-2026-56391 | coreutils | Moderate | Fix deferred | uniq multibyte over-read requiring specific CLI options and input |
| CVE-2026-8674 | glibc | Moderate | Fix deferred | Long DNS resolver search domains can abort a process |
| CVE-2026-86805 | glibc | Moderate | Affected | Local dynamic-loader race requiring local setuid, path, and hardlink conditions |
| CVE-2026-89092 | glibc | Moderate | Affected | nscd DNS caching daemon issue; the nscd package is not installed in the images |
| CVE-2026-95818 | glibc | Moderate | Affected | Dynamic-loader stack overflow requiring specific local setuid and path conditions |
| CVE-2026-97399 | glibc | Low | Affected | Power8-specific code path; published images target amd64 and arm64 only |
| CVE-2021-46195 | gcc | Low | Fix deferred | libiberty symbol-demangler recursion (the mingw-gcc update listed in the advisory does not apply to runtime libgcc) |
| CVE-2022-27943 | gcc | Low | Fix deferred | libiberty defect; Red Hat notes that libgcc and libstdc++ do not contain the affected code |
| CVE-2026-95619 | gcc | Moderate | Affected | libstdc++ aligned-allocation overflow mapped to libgcc via the gcc SRPM |
| CVE-2026-102010 | gcc | Moderate | Affected | Optional libstdc++ pb_ds container defect mapped to libgcc via the gcc SRPM |
| CVE-2023-50495 | ncurses | Low | Fix deferred | tic terminal-compiler utility defect; not present in libncurses |
| CVE-2022-41409 | pcre2 | Low | Fix deferred | Affects pcre2-tools CLI utilities only; not shipped in UBI |
Build-stage and non-CVE advisories
- Alpine build-stage
curl/libcurlremains8.22.0-r0: As of 2026-10-23, the Alpine package indexes for amd64 and arm64 have not yet published8.23.0. This host package is used only inside the temporary build stage to fetch SHA-256-pinned source archives; the runtime image ships the newly compiled staticcurl 8.23.0. - GO-2026-5932:
golang.org/x/crypto/openpgpis deprecated upstream with no fixed version and no CVE alias. Whilegolang.org/x/cryptoremains in the module graph, none of the target builds import the affectedopenpgppackages.
Historical repairs, not newly fixed vulnerabilities
Module-path binary scanners continue to match SILO against CVE-2025-27414, CVE-2026-33322, CVE-2026-33419, CVE-2026-34204, CVE-2026-39414, CVE-2026-40344, CVE-2026-41145, and CVE-2026-42600 because SILO retains the github.com/minio/minio module path and those upstream advisories specify open-ended version ranges. All eight issues were patched and verified in earlier SILO releases; see SFTP, OIDC, LDAP, replication metadata, CSV, Snowball, signature validation, and ReadMultiple.
Compatibility and dependency regression evidence
To prevent reproduced regressions in CLI table formatting, character-width calculation, and NetBSD cross-compilation, this batch pins tablewriter 0.0.5, go-humanize 1.0.1, go-runewidth 0.0.29 in the root modules, and go-systemd/v22 22.6.0 (while xattr retains its existing width-library combination). In addition:
- Kafka notification targets explicitly initialize the protocol version default to
2.1before applying user settings; - DARE encryption retains 32-byte keys and v2 writes while fixing buffer handling when unsealing legacy v1 payloads;
pgzipretains the integrity check that returnsunexpected EOFwhen a gzip checksum trailer is missing.
The October 22–23 candidate validation (built with Go 1.27.3 on both the rollback baseline and the candidate, with persisted-data acceptance run on a single-node four-drive Linux arm64 setup) passed:
- Full compilation and applicable
-raceand lint suites; - Encrypted-object read and rollback verification across the old and new dependency stacks;
- Service restart, missing-shard healing recovery, and CLI output checks;
- End-to-end Console object uploads/downloads, ZIP archive previews, WebSocket sessions, and metrics charts.
The candidate sources with the October 24 dependency updates passed these additional checks:
- Full race suites for silo-pkg, mcli, and KES; API / pkg / cmd race suites for Console; the full SILO test suite and focused SFTP, IAM, and Parquet race tests. Lint, module verification, and dependency tidiness checks passed for all five projects.
- SFTP object reads, writes, listing, deletion, Unicode names, transfer block boundaries, and read-only data protection through the SILO driver against HTTP / HTTPS S3 backends. Upstream read-only server and symlink deletion regression tests passed. Both dependency baselines passed the new object-operation tests.
- JWX signature and time-claim boundary comparisons; 68 CLI output comparisons with no new differences, normalizing only existing metadata map ordering.
- Local etcd mTLS, account authentication, transactions, watches, leases, and old → new → old clients reading across server restarts; NATS / STAN message persistence, ACKs, durable subscription cursors, and reconnection.
- Local Azure / GCS protocol fixtures, KES GCP Secret Manager operations and pagination, the Azure SDK’s Arrow decoder tests, and the full upstream procfs parser fixtures. The procfs update corrects bcache units and NVMe sizes, so the corresponding metric values may change to reflect those corrections.
- Native Darwin arm64 and cross-compiled Linux amd64 builds of mcli, Console, SILO, and KES; NetBSD amd64 cross-builds of mcli and SILO.
Linux amd64 package-level Go vulnerability scans of all five projects found no affected imported packages (only the module-level GO-2026-5932 entry remains, with no imports of the affected OpenPGP subpackages).
2 - SILO 20261018 Release Notes
Server 20261018 is available with versioned archives, native packages, and standard/distroless Docker Hub images for amd64 and arm64. It supersedes Server 20260916 (20261015 is retained as a superseded historical draft record).
| Identity | Value |
|---|---|
| Release tag | RELEASE.2026-10-18T00-00-00Z |
| Package version | 20261018000000.0.0 |
| Release source | 0cd388c666f8211f552604ae73448a066e9c25ce |
| Previous public source | 2a4d51406b7ed87af5fe6fe0f801f3290f96eb3c (Server 20260916) |
| Complete change set | 20260916 → 20261018 |
See the download page for versioned archives, native packages, and pinned container commands.
Object Lock and IAM security
SN-2026-015 / SN-2026-016 — Object Lock retention authorization and in-place overwrite protection. #234 and #235 repair retention authorization and in-place overwrites:
- Every
PutObjectRetentionrequest evaluatess3:PutObjectRetentionwith the request’s Object Lock condition keys before looking up the target object. Shortening, clearing, or changing the mode of an activeGOVERNANCEretention additionally requires bothx-amz-bypass-governance-retention: trueands3:BypassGovernanceRetention(which supplements rather than replacess3:PutObjectRetention). Clearing retention evaluates remaining retention days as0, and activeCOMPLIANCEretention cannot be shortened or changed. - Caller-selected write
versionIdparameters require trusted replication credentials (s3:ReplicateObject). Directorynullversions and persisted multipart targets are re-verified under the destination write lock, and unreadable retention metadata fails closed while preserving existing legal holds. Upgrade all writing nodes and every replication site accepting S3 requests. See the security advisory and repair design.
SN-2026-017 — Admin credential self-service authorization. #242 ensures credential self-inspection and cleanup respect explicit Deny statements, session-policy restrictions (sessionPolicy), and external authorization plugin decisions. Under built-in authorization, restricted session credentials cannot create or update service accounts broader than their own scope, and non-root administrators cannot mutate root’s derived credentials; unrestricted empty-policy inheritance and cleanup of damaged non-root keys remain supported. Follow-up commit 995439260 normalizes bulk self-listing aliases to the parent identity and preserves self-inspection for ordinary users without attached policies, requiring no new Admin actions or IAM storage migration. See the security note and authorization contract.
Scoped STS revocation
0cd388c66 updates RevokeTokens to enumerate durable STS identity records alongside in-memory caches, allowing nodes that missed an earlier token-issuance notification to revoke matching tokens reliably. Committed token tombstones are broadcast to sibling nodes and site-replication peers even if another token in the batch fails to save, and offline peers converge via IAM deletion healing. Object-backed and etcd-backed cache refreshes also evict revoked tokens after missed notifications or watches.
The revocation scope remains the requested parent identity and optional token type across the cluster’s snapshot prior to the operation cutoff, leaving newly issued tokens, unrelated users, and parent policies untouched. Durable enumeration adds work only to the administrative revocation path, not to per-request authentication. See the STS revocation design and cost analysis.
Rebalance and pool decommission consistency
60c7179dd (incorporating #248) and 1748f62c7 prevent rebalance and pool-decommission workflows from overwriting a newer object copy in another pool with an older migrated copy. Migration re-verifies source versions (including multipart completions and tiered-object metadata) under the shared object lock, preserves null and UUID delete markers, and removes only confirmed migrated versions rather than deleting the entire source prefix. Concurrent writes, deletes, or destination changes safely abort cleanup for a later retry.
Before retiring a pool, verify object versions, tags, and delete-marker state as described in multi-pool object consistency (independent cross-site delete-marker ordering remains tracked in #217).
Ordinary object listing recovery
#239 (incorporating the original #221 work and closing #218) fixes an issue where ListObjects / ListObjectsV2 could succeed while omitting readable keys during eligible cross-pool metadata disagreements. The pool merger now re-verifies uncertain same-name entry groups under the corresponding object read locks: it returns current readable metadata, omits confirmed absent keys, or returns an explicit error such as 503 SlowDownRead when quorum cannot establish either state. A failed pagination producer fails the scan; clients must consume all pages and treat any page error as an incomplete listing.
Resolved entries retain their lock-free fast path. Recovery applies only to eligible latest-object groups without lowering quorum or adding persistent metadata; request cancellation interrupts lock backoff, and identified recovery lock conflicts wait for a single bounded interval. Follow-up commits 3dd53eb81 and 2080b3d03 preserve directory and CommonPrefixes entries when a colliding object name is confirmed absent. Measured latency and lock-cost benchmarks are documented in listing recovery lock cost.
Multipart upload listing and cleanup
Default disk scan rolled back to legacy in-memory listing. The default disk-scan implementation merged on October 17 (#243) was withdrawn by 4733e1423. Public ListMultipartUploads uses the node-local in-memory cache when prefix is empty and performs an exact-key lookup when prefix is nonempty; process restarts or requests routed to different nodes can omit in-flight uploads (scalable persistent enumeration remains tracked in #79).
Compared with 20260916, MINIO_API_MULTIPART_LISTING=strict is retired: setting the environment variable logs a startup warning and is ignored (the legacy shared api multipart_listing config key is likewise tolerated and ignored). However, the 20260916 upload-creation identity metadata, uploadId path validation, abort target matching, and post-abort cache/peer notifications remain active. The read-only multipart-preflight Admin diagnostic still requires admin:StorageInfo and reports mode=legacy, applying its entry, timeout, and concurrency budgets strictly to explicit diagnostic calls. See the multipart listing design, rollback rationale, and capacity benchmarks and the configuration reference.
Background stale-upload cleanup (default 24-hour expiry checked every 6 hours) includes two repairs:
- #236 parses native upload-directory timestamps before falling back to Base64 decoding, avoiding unnecessary disk metadata reads.
0f9199e0cremoves empty upload-parent directories immediately after deleting expired child upload directories, rather than waiting another 24-hour cycle after child deletion updates the parent’smtime(nonempty parents and active uploads remain protected).
Error classification, metadata reuse, and smaller repairs
| Change | User-visible result |
|---|---|
| #232: missing staged commit metadata | PUT and multipart completion return retryable 503 SlowDownWrite when staged metadata disappears before final commit, rather than misreporting IncompleteBody or NoSuchKey; storage RPCs and healing preserve this error class. |
| #233: reused metadata and scanner structs | Clear absent legacy EcM / Signature fields when decoding into pooled xl.meta structs and clear mutually exclusive scanner update/final fields (see stale metadata reuse). |
| #225, #229, #238: checksum error wording | Align Content-MD5 capitalization and algorithm-specific checksum mismatch messages with AWS S3; known algorithms are named in PUT / UploadPart errors while unknown algorithms retain the generic message. |
| #238: embedded Console error unwrapping | Preserve wrapped S3 error codes so attempting to suspend versioning on an Object Lock bucket surfaces InvalidBucketState cleanly in the UI. |
| #220: Lambda target sets | Fix the inverted TargetIDSet.IsEmpty boolean check. |
| Erasure-coding documentation | Clarify parity and drive-failure tolerance per erasure set and stored object layout (see storage classes). |
Coordinated components and dependencies
| Component | Selected identity |
|---|---|
| Embedded Console | v2.4.2 (v0.0.0-20261017112417-ef2dd2c41d21) |
| MC module / image-bundled mcli | 20261018 (v0.0.0-20261017101407-fa3b8bf72458) |
| Server shared package | github.com/pgsty/silo-pkg/v3 v3.14.3 |
| Upstream S3 SDK | github.com/minio/minio-go/v7 v7.4.1 |
| Go toolchain | 1.27.2 |
| Optional external key service | PGSTY KES 20261018 (standalone service at 2f7e741db905) |
Standalone Console v2.4.2 and mcli 20261018 declare silo-pkg v3.14.2, while Server’s module graph selects v3.14.3 (which optimizes admin-action classification without altering authorization semantics) and explicitly requires the maintained Console and MC modules.
minio-go v7.4.1 adds failed-upload cancellation cleanup, UTC retention timestamps, and preserved STS durations. Dependency updates include go-jose v4.1.6, ldap v3.4.15, compress v1.20.3, msgp v1.6.5, etcd v3.7.2, and golang.org/x/*. Note: go-jose v4.1.6 rejects OIDC/JWK RSA keys larger than 4096 bits; verify OIDC discovery, JWKS, and login flows before upgrading if your identity provider uses oversized RSA keys (see the dependency security note).
Release tooling pins GoReleaser v2.18.3, nFPM v2.48.0, golangci-lint v2.15.0, and govulncheck v1.9.0, with Object Lock race regressions gating release builds. make rebrand-guard now uses git grep and reports read errors explicitly without requiring ripgrep (7007c164a). Because the bundled Helm chart still defaults to the 20260916 Server image tag, override the image tag explicitly when deploying 20261018 via Helm.
Upgrade and rollback checklist
- Back up state and pin artifacts: record your installed versions and pin verified binary or image digests. Follow the migration guide and IAM recovery procedure to back up complete IAM storage, configuration, and encryption keys (standard live IAM exports omit revocation tombstones).
- Coordinate cluster cutover: upgrade all Server nodes and replication sites together; do not mix old and new binaries against the same IAM backend or perform rolling cross-version downgrades.
- Verify Object Lock and service-account policies: test retention updates, legal holds, service-account creation/updates, explicit self
Denyrules, and root credential management with the exact credentials used by your automation. Reissue previously damaged expiring keys through an authorized parent identity. - Remove
strictmultipart listing dependencies: ifMINIO_API_MULTIPART_LISTING=strictwas enabled on 20260916, ensure clients track their own activebucket/key/UploadIDtuples for completion or cancellation rather than relying on publicListMultipartUploadsfor full discovery. - Check listing, retry handling, and OIDC/TLS paths: verify ordinary and paginated
LIST, client retry policies for503 SlowDownWriteon PUT/completion, OIDC/JWKS RSA key sizes, and outbound connections across TLS-inspecting middleboxes (see the Go 1.27 TLS and OIDC guide). - Rollback considerations: downgrading to 20260916 reintroduces the October Object Lock and admin credential vulnerabilities, and upgrading does not retroactively restore versions or tags overwritten prior to the upgrade.
Artifact verification and acceptance record
The signed tag RELEASE.2026-10-18T00-00-00Z and all 36 release assets build from 0cd388c666f8211f552604ae73448a066e9c25ce and passed Go CI, VulnCheck, Release, and package finalization.
- Static artifact verification: verified all asset digests, 6 archive version stamps, 6 native package payloads (RPM/DEB/APK), 34 build provenance attestations, 2 Sigstore checksum signatures, 2 RPM package signatures, and 12 SBOMs.
- Functional and fault-recovery acceptance: exercised native Darwin and Linux S3/IAM/STS/Object Lock suites, LDAP/OIDC, in-place upgrade from 20260916, bucket and site replication, 3-site recovery with Console, 3-site and 4-process shared-cluster STS revocation, and 4 multi-pool migration scenarios (2/3 pools × rebalance/decommission). An 8-process, 2-pool cluster completed 2,896 full listing scans under rolling restarts and 1,270 full listing scans under fault-recovery load with zero missing keys, duplicates, or corrupted reads; 3 Linux package lifecycle suites and 8 container runtime variants also passed (Windows and Darwin amd64 artifacts were verified statically, and amd64 containers ran under emulation).
- Published Docker Hub digests:
- Standard image (and
latestat publication):sha256:2636fc4155d9a7e41ff58cdb65ddd13ba9304e67184e13c25bc5cb34016269d7 - Distroless image:
sha256:472f59c7be537571f0f9a2ee6b8fe033a220939d521255bd4bf5339e53db7218
- Standard image (and
Thanks to szopi00 for reporting the Object Lock issues; mr javad seydi (@mrjavadseydi) for the initial LIST and multipart work; Julien Laurenceau (@julienlau) for the commit-metadata classification and struct-reuse fixes; clown15 for upload-directory cleanup; cbornet and yupoet for checksum error message improvements; AleksaMCode for the Lambda target-set fix; and Denis Babchuk (@botdeniska) for the #248 migration overwrite fix and silo-pkg optimization.
3 - silo-pkg 3.14.3 Released
Release: v3.14.3 · Source: e9d585ba1e7a. See the v3.14.2 release notes for the October dependency baseline.
github.com/pgsty/silo-pkg/v3 v3.14.3 reduces the cost of classifying admin: actions during IAM policy evaluation and is selected by Server 20261018.
AdminAction.IsValid and HasResource now perform exact map lookups before scanning the action sets: literal misses return immediately, and wildcard patterns whose prefix cannot match the admin: namespace skip the validation scan, while patterns such as adm* and a*d*m*i*n* retain their exact existing behavior.
Performance and validation
Across six alternating single-CPU benchmark runs on an Apple M5 Max (Go 1.27.2), median evaluation time for a policy with 100 non-matching bucket grants followed by a matching grant dropped from 303.2 µs to 54.4 µs (~82% reduction) with zero additional heap allocations.
Validation covered Go 1.26 and 1.27 race suites, lint, LDAP integration, vulnerability scanning, 60 seconds of equivalence fuzzing, and an exhaustive comparison across 50,000 policies and 970,020 policy evaluations against v3.14.2; mcli 20261018 also produced identical results against server builds using v3.14.2 and v3.14.3.
Adoption
Public APIs, policy formats, authorization decisions, dependencies, and the go 1.26 floor are unchanged. Server 20261018 adopts v3.14.3, while standalone mcli 20261018 and Console v2.4.2 remain on v3.14.2 (see the component matrix).
Thanks to Denis Babchuk (@botdeniska) for PR #11, and Klaus Post (@klauspost) for the original optimization.
4 - PGSTY KES 20261018 Released
Release: RELEASE.2026-10-18T00-00-00Z · Package: 20261018000000.0.0 · Source: 2f7e741db905.
This is the first maintained PGSTY release of the Key Encryption Service (KES). KES is a standalone stateless service for SILO deployments that require external KMS-backed server-side encryption (see the component matrix).
Changes
- Establish PGSTY maintenance and release pipelines while preserving upstream copyright, attribution, and third-party licenses. The executable and system packages remain
kes, and the Go module path remainsgithub.com/minio/kes. - Build with Go 1.27.2, refresh dependencies, and migrate the AWS Secrets Manager backend from the deprecated AWS SDK for Go v1 to AWS SDK for Go v2. Preserve YAML configuration fields, explicit credential precedence, custom endpoints, and stored key formats (continuing to ignore
~/.aws/configand SDK endpoint environment overrides). - Preserve the KES HTTP API,
MINIO_KMS_KES_*configuration settings, existing root keys, and encrypted objects so existing SILO + KES deployments can upgrade in place without key migration. - Add upgrade, rollback, and restart integration suites pairing SILO with OpenBao KV v2 and the Moto Secrets Manager emulator, restarting SILO at each version transition so in-memory key caches cannot mask compatibility regressions.
- Ship signed Git tags, GitHub build provenance attestations, GPG-signed RPMs, and non-root container images, with the built-in
kes updateself-updater disabled.
Deployment and upgrade
In a server-side encryption deployment, SILO encrypts object contents, KES authenticates mTLS client requests and wraps or unwraps data encryption keys (DEKs), and OpenBao persists the root key encryption keys (KEKs). Point MINIO_KMS_KES_ENDPOINT at KES, not OpenBao (see the KES and OpenBao design note for architecture boundaries and recovery requirements).
Configure TLS certificates, mTLS client identity policies, and a persistent key store before starting KES in production (development mode stores keys only in memory). Official containers run as non-root UID/GID 1000:1000, so mounted configuration files and certificates must be readable by that user; Linux RPM/DEB/APK packages install /usr/local/bin/kes without starting an unconfigured service.
Back up your key store and configuration before upgrading, then verify decryption of existing objects, creation of new encrypted objects, and cold readback after restarting both KES and SILO.
Delivery and validation
All 19 release assets (6 Linux/macOS/Windows amd64/arm64 archives, 6 Linux RPM/DEB/APK packages, an archive checksum manifest, and 6 package checksum sidecars) were verified across downloaded digests, binary source/platform stamps, package payloads, both RPM signatures, and 11 GitHub build attestations (release workflow).
The official multi-arch (linux/amd64 and linux/arm64) container image docker.io/pgsty/kes:RELEASE.2026-10-18T00-00-00Z (Docker publication run) has index digest:
The primary verified integration stack is SILO + PGSTY KES + OpenBao KV v2 (covering mTLS authentication, OpenBao AppRole access, DEK wrap/unwrap, restart persistence, unauthenticated request rejection, and interoperability/rollback matrices across SILO 20260903/20260916 and upstream KES 2025-03-12T09-35-18Z, with AWS Secrets Manager protocol and storage regression coverage via Moto).
Download KES · Configuration and installation · Artifact verification · Changes since upstream.
5 - SILO Console 2.4.2 Released
Release: v2.4.2 · Source: ef2dd2c41d21.
Console v2.4.2 ships both as a standalone release and as the embedded Console in Server 20261018 (see also the silo-pkg v3.14.2 release notes).
Changes
- Adopt mcli 20261018, silo-pkg v3.14.2, and upstream minio-go v7.4.1. The SDK cleans up failed multipart uploads after cancellation, formats retention timestamps in UTC, and preserves requested STS durations (parallel GET and ComposeObject remain opt-in).
- Preserve wrapped S3 error codes during bucket-versioning operations so attempting to suspend versioning on an Object Lock bucket surfaces
InvalidBucketStatecleanly. - Build with Go 1.27.2 and Node 24.22.0 LTS. Refresh
golang.org/x/*modules alongside compress v1.20.3, msgp v1.6.5, etcd v3.7.2, fwd v1.2.1, and cpuid v2.4.1 (retaining go-systemd v22.6.0 for NetBSD and tablewriter v0.0.5 for MC API compatibility). - Pin GoReleaser v2.18.3, golangci-lint v2.15.0, and govulncheck v1.9.0, rebuild embedded frontend assets as Console 2.4.2, and update pinned container build stages.
Integration
Because the retained module path has no /v2 suffix, Go consumers reference Console v2.4.2 via pseudo-version v0.0.0-20261017112417-ef2dd2c41d21. An embedding Server must declare both Console and MC replacements explicitly:
Console v2.4.2 requires github.com/pgsty/silo-pkg/v3 v3.14.2 (see the password-permission migration guide). Note that the go-jose v4.1.6 4096-bit RSA JWK limit applies only to Server OIDC paths that import go-jose; neither go-jose nor go-ldap is linked into the standalone Console binary.
Delivery and validation
All 44 release assets (6 platform binaries, 6 archives, 9 Linux packages, checksum manifests, SBOMs, and signed provenance attestations) were verified against tagged commit ef2dd2c41d21. The commit passed the full CI matrix (permissions, SSO, replication, browser tests, source-image equivalence, and reproducible packaging), vulnerability checks, the local Go race suite, 251 frontend unit tests, and live API/browser sharing checks against Server 20261018.
The published docker.io/pgsty/silo-console:v2.4.2 multi-arch (linux/amd64 and linux/arm64) manifest digest is:
Release verification contract · Component matrix · Changes since v2.4.1.
6 - mcli 20261018 Released
Release: RELEASE.2026-10-18T00-00-00Z · Package: 20261018000000.0.0 · Source: fa3b8bf72458.
mcli 20261018 ships both as the standalone CLI client and as the coordinated client module in Server 20261018 and Console v2.4.2 (see also the silo-pkg v3.14.2 release notes).
Changes
- Adopt silo-pkg v3.14.2 and upstream minio-go v7.4.1. The SDK cleans up failed multipart uploads after cancellation, formats retention timestamps in UTC, and preserves requested STS durations while retaining existing
CopyObjectembedded-error and checksum handling (parallel GET and ComposeObject remain opt-in). - Build with Go 1.27.2. Refresh
golang.org/x/*modules, compress v1.20.3, msgp v1.6.5, and etcd clients v3.7.2 (with fwd v1.2.1 fixing reads that return bytes alongside an error as well asSkipoffsets, and cpuid v2.4.1 fixing cache detection). - Pull in go-jose v4.1.6 (fixing excessive CPU usage on oversized RSA JWKs in GHSA-mgcc-cww5-fp2q) and LDAP v3.4.15 via
silo-pkg. - Refresh pinned UBI 9 base images and release tooling: GoReleaser v2.18.3 (including log secret redaction and archive fixes), nFPM v2.48.0, golangci-lint v2.15.0, govulncheck v1.9.0, and gofumpt v0.12.0.
Upgrade
CLI commands, aliases, MC_* environment variables, and configuration formats are unchanged (retaining the go-systemd v22.6.0 replacement for NetBSD builds and tablewriter v0.0.5). See the component matrix for version pairings.
Delivery and validation
All 19 release assets (6 Linux/macOS/Windows amd64/arm64 archives, 6 Linux RPM/DEB/APK packages, an archive checksum manifest, and 6 package checksum sidecars) were verified across downloaded digests, 6 binary source stamps, 6 package payloads, PGSTY GPG signatures (9592A7BC7A682E7333376E09E7935D8DB9BD8B20), and 11 GitHub build attestations bound to fa3b8bf72458.
The tagged commit passed Go unit tests, cross-compilation, vulnerability scanning (zero reachable or imported-package vulnerabilities; the unused OpenPGP module-only advisory GO-2026-5932 remains), and live Go/Shell TLS functional suites against SILO fbcb5119782a (release build · Docker publication).
The published docker.io/pgsty/mc:RELEASE.2026-10-18T00-00-00Z (and latest at publication) multi-arch (linux/amd64 and linux/arm64) manifest digest is:
Download and installation · Full changelog · Changes since 20260916.
7 - silo-pkg 3.14.2 Released
Release: v3.14.2 · Source: c6835145176f.
github.com/pgsty/silo-pkg/v3 v3.14.2 provides the October dependency baseline for mcli 20261018 and Console v2.4.2; follow-up patch v3.14.3 adds an admin-action classification optimization used by Server 20261018.
Changes
- Update upstream
minio-goto v7.4.1: clean up failed multipart uploads even after caller context cancellation, serialize Object Lock retention timestamps in UTC, preserve requested STS durations, and support projected Kubernetes service-account token files (parallel GET and ComposeObject remain opt-in). - Update
go-joseto v4.1.6: fix excessive CPU consumption when parsing oversized RSA JWKs (GHSA-mgcc-cww5-fp2q); update LDAP to v3.4.15, msgp to v1.6.5, and etcd clients to v3.7.2. - Refresh
golang.org/x/*:x/cryptov0.58.0,x/netv0.61.0,x/sysv0.49.0, andx/textv0.43.0. Built and tested with Go 1.27.2 while retaining thego 1.26module floor and thego-systemd v22.6.0NetBSD replacement.
Compatibility and adoption
Public package signatures and password-policy semantics are unchanged (see the password-permission migration guide when upgrading from before v3.14.0, and the component matrix for version pairings).
Behavioral notes:
go-jose v4.1.6rejects RSA JWKs larger than 4096 bits; verify identity-provider key sizes in OIDC/JWKS consumers.- STS durations below one hour are now sent as requested rather than silently clamped by the client, so invalid short durations will be rejected by the server.
Validation
The release passed full lint and race suites under Go 1.27.2, race tests under Go 1.26.9, OpenLDAP integration tests, module tidiness checks, and govulncheck scanning (zero reachable or imported-package vulnerabilities).
8 - SILO 20261015: Superseded Draft Record
Status: Superseded by Server 20261018. RELEASE.2026-10-15T00-00-00Z was briefly published at 2026-10-15 13:52:28 (UTC+8), then reverted to Draft and superseded by Server 20261018, which includes the complete October repair and component stack. This page archives the candidate source scope and acceptance test results; use 20261018 for all deployments and upgrades.
- Historical tag:
RELEASE.2026-10-15T00-00-00Z - Package version:
20261015000000.0.0 - Candidate source:
7a3ca0f82100cce06289a465f54139da2639f642(full diff against Server 20260916)
Object Lock repairs
- SN-2026-015 — Retention authorization (#234): every
PutObjectRetentionrequest requiress3:PutObjectRetention, evaluated with request-body Object Lock condition keys before reading object state. Shortening, clearing, or changing the mode of activeGOVERNANCEretention additionally requires the explicit bypass header ands3:BypassGovernanceRetention(which does not override aDenyons3:PutObjectRetention). Clearing retention evaluates remaining days as0, and distant future dates no longer saturate duration arithmetic. - SN-2026-016 — Protected version writes (#235): caller-selected write
versionIdparameters require trusted replication credentials (s3:ReplicateObject). Directorynullversions and persisted multipart targets are checked under the destination write lock before overwrite (including uploads initiated before upgrading or recovered from legacy staging state), enforcing activeGOVERNANCE,COMPLIANCE, and legal-hold protections; unreadable lock metadata fails closed. - Release gate (#237): the release workflow runs the Object Lock race regression suite as a build prerequisite, covering slow-body directory overwrites, preflight read failures, legacy multipart completions against
COMPLIANCE, and recovered-upload UUID collisions.
See the security advisory and repair design for full details.
Other changes since 20260916
| Change | User-visible effect |
|---|---|
| #233 | Clear absent fields when decoding reused legacy xl.meta headers and keep scanner update/final responses mutually exclusive (see stale metadata reuse). |
| #225 | Align checksum error messages with AWS S3, including Content-MD5 capitalization. |
| #220 | Fix the inverted TargetIDSet.IsEmpty check for Lambda notification target sets and add regression coverage. |
fa565ec9f |
Upgrade to Go 1.27.2, updated golangci-lint, and golang.org/x/net v0.60.0 across module requirements and pinned Docker build stages. |
Component identities
| Component | Selected version |
|---|---|
| Server | RELEASE.2026-10-15T00-00-00Z (package 20261015000000.0.0) |
| Embedded Console | v2.4.1 (source 1360e26d976d) |
| mcli | RELEASE.2026-09-16T00-00-00Z (source e952aa78f10a) |
| Shared package | github.com/pgsty/silo-pkg/v3 v3.14.1 |
| Upstream S3 SDK | github.com/minio/minio-go/v7 v7.3.1-0.20260915093545-32e1f32cb176 |
| Go toolchain | Go 1.27.2 |
Upgrade requirements
- Upgrade all writers and replication peers: upgrade every Server node and all replication sites accepting S3 writes so unpatched peers cannot accept unauthorized retention changes or in-place overwrites.
- Review retention policies: policies granting
s3:BypassGovernanceRetentionwithouts3:PutObjectRetention, or relying on bypass to evade retention condition keys, now receive403 AccessDenied. - Caller-selected
versionIdrestriction: ordinary S3 writes (including root-signed requests) cannot specify a targetversionId; trusted replication preserves version IDs, andmcli batch replicateinto SILO requiress3:ReplicateObjecton the destination credentials. - Fail-closed lock checks: locked directory objects and locked multipart targets cannot be overwritten; if lock metadata is unreadable, restore drive health before retrying.
- Audit historical state: review prior audit logs for unexpected retention changes before the upgrade (when upgrading from releases prior to 20260916, also follow the September upgrade requirements).
Candidate artifact acceptance
The following CI workflows and verification checks passed against 7a3ca0f82100cce06289a465f54139da2639f642:
| Check | Result | Workflow run |
|---|---|---|
| Main Go CI | Passed | Run 38686240587 |
| Vulnerability check (VulnCheck) | Passed | Run 38686240625 |
| Release build and Object Lock gate | Passed | Run 38686425467 |
| Finalize release packages | Passed | Run 38687734395 |
- Static artifact verification: verified all 36 release assets, 34 build provenance attestations, static version/commit identities across all 6 Linux/macOS/Windows
amd64/arm64binaries, payloads across all 6 RPM/DEB/APK packages, and both RPM signatures (see historical archive checksums and package checksums). - macOS arm64 runtime acceptance (110/110 passed):
| Scenario | Passed |
|---|---|
| Fresh installation, full Object Lock API suite | 49/49 |
| In-place upgrade over data created by the official 20260916 binary | 12/12 |
| Full Object Lock API suite after upgrade | 49/49 |
(Across those runs, all 65 rejected operations left the complete target object state unchanged; Windows and macOS amd64 binaries were verified statically.)
Docker images
Docker publication run 38689158028 built and attested the multi-platform (linux/amd64 and linux/arm64) images from the same release commit:
| Variant | Version tag | Multi-platform index digest |
|---|---|---|
| Classic | RELEASE.2026-10-15T00-00-00Z |
sha256:aaac6bce13eb99316b3c88bd20143fc3c38ab607585256a411a095c44ee57f62 |
| Distroless | RELEASE.2026-10-15T00-00-00Z-distroless |
sha256:70cac541c5a928be5cd16d6b8732d85ac06e891a0e3d9956a745381330692fc6 |
All four platform-specific images (whose /usr/bin/silo binaries matched the official Linux archive binaries byte for byte) passed /minio/health/ready probes and the full Object Lock API suite (196/196 passed, with all 120 rejected operations preserving object content, version identity, ETag, retention, legal hold, and version listings):
| Image | Runtime environment | API checks | Binary comparison |
|---|---|---|---|
Classic linux/arm64 |
Native ARM64 on Docker Desktop ARM64 | 49/49 | Byte-identical to Linux arm64 archive |
Classic linux/amd64 |
AMD64 emulation on Docker Desktop ARM64 | 49/49 | Byte-identical to Linux amd64 archive |
Distroless linux/arm64 |
Native ARM64 on Docker Desktop ARM64 | 49/49 | Byte-identical to Linux arm64 archive |
Distroless linux/amd64 |
AMD64 emulation on Docker Desktop ARM64 | 49/49 | Byte-identical to Linux amd64 archive |
Combined with the native macOS arm64 installation and upgrade suite, candidate runtime acceptance passed 306/306 checks.
9 - SILO Console 2.4.1 Released
SILO Console v2.4.1
was released on September 16, 2026 from
1360e26d976d.
It integrates pkg v3.14.1 and mcli 20260916, restricts the anonymous sharing proxy,
streams multi-object ZIP downloads and adds signed, traceable release artifacts.
Shared downloads
The fix for #52 restricts the anonymous proxy to object-content GET requests at the configured S3 origin. It rejects redirects, system paths and query-selected operations that are not object downloads. Public objects, presigned links and version-specific downloads continue to work. No new sharing-disable environment variable is needed.
Thanks to Jiri Pejchal (@jiri-pejchal) for reporting the internal-metrics exposure. See the sharing boundary for the supported request forms and reverse-proxy requirements.
Password-policy migration
This patch release includes an authorization change. The Change Password
button and session capability use admin:ChangeMyPassword; creating users and
resetting another user’s password continue to use admin:CreateUser.
With the matching SILO Server, denying CreateUser alone no longer locks the
caller’s own password. To retain that restriction, deny both actions in the
same statement before upgrading, keeping its original scope and conditions.
The updated built-in readonly permits self-service password changes and no
longer overrides a separate CreateUser Allow. Saved policy overrides retain
their stored statements. Upgrade Server, Console and pkg together and review
the migration and rollback guide.
Downloads and browser recovery
- Multi-selection ZIP downloads stream to a file writer when the browser supports it, with backpressure, cancellation and duplicate-transfer protection. Other browsers hand the download to their native download manager. Neither path buffers the complete ZIP in JavaScript.
- Progress uses a percentage only when the response supplies a reliable total. The native fallback identifies the handoff explicitly; cancellation then belongs to the browser’s download manager. Selections over 5 GiB or with an unknown total recommend mcli for long-running transfers.
- Invalid routes and rendering failures offer recovery without clearing preferences. English and Chinese labels, icon controls, sign-out text and keyboard-accessible tooltips are completed.
Dependencies and embedding
| Component | Selected version |
|---|---|
| Shared package | github.com/pgsty/silo-pkg/v3 v3.14.1 |
| MC source | github.com/pgsty/mc v0.0.0-20260916070421-e952aa78f10a — mcli 20260916 |
| Upstream minio-go | v7.3.1-0.20260915093545-32e1f32cb176 |
| JWX / strfmt | v3.3.0 / v0.27.2 |
| React Router | v7.18.4 |
| Build tools | Go 1.27.1, Node 24.21.0, Yarn 4.13.0 |
The SDK handles S3 errors embedded in HTTP 200 CopyObject responses; JWX fixes custom JSON field-name escaping and strfmt updates hostname validation for Go 1.27. The embedded frontend and third-party credits are regenerated from this dependency graph. go-systemd v22.6.0 and tablewriter v0.0.5 remain intentional compatibility pins.
Go embedders select Console as
github.com/pgsty/silo-console v0.0.0-20260916075814-1360e26d976d through the
historical github.com/minio/console module replacement. Go does not inherit
dependency replacements: copy the explicit MC replacement from the
tagged README.
The supported administration target is the coordinated SILO stack; upstream
MinIO/MC compatibility is best effort.
Packages and verification
The GitHub Release contains 44 assets: binaries and bundles, DEB/RPM/APK packages, source, legal notices, SPDX SBOMs, a checksum manifest and its Sigstore bundle. Checksums use Cosign signatures, and provenance records the tagged workflow inputs.
For standalone v2.4.1 deployments, use the GitHub Release binaries or Linux packages; multi-architecture Docker Hub images on docker.io/pgsty/silo-console are available starting with Console v2.4.2. Server-embedded Console is unaffected.
Linux packages retain minio-console.service, console-user and
/etc/default/console. The service now uses /var/lib/silo-console for state,
/etc/silo-console/certs for certificates, and a 90-second shutdown limit.
Before restarting an existing installation, migrate certificates with the
documented ownership or retain the old path through CONSOLE_OPTS. The installer
preserves old certificates and keys and does not restart the service for you.
See the package upgrade instructions.
The exact release source passed the complete CI matrix, vulnerability checks and release verification. Local validation also covered 251 frontend unit tests, 51 browser tests, byte-identical embedded assets and sharing in standalone and embedded deployments. The downloaded Darwin/arm64 executable was independently checked against the signed checksum manifest and exercised against a SILO test server.
10 - silo-pkg 3.14.1 Released
v3.14.1 was published from
fa657ef4.
This is a dependency and tooling release of github.com/pgsty/silo-pkg/v3;
the diff from v3.14.0
changes no package Go source or public API.
Dependency changes
- JWX advances from v3.2.0 to v3.3.0. The vendor’s release notes
identify GHSA-4cf7-xm37-g63h: custom JSON field names must be escaped during
encoding. Reachability depends on accepting attacker-controlled custom claim
names; this package’s
envJWT path uses registered claim names. - Upstream minio-go advances to
v7.3.1-0.20260915093545-32e1f32cb176, including minio-go #2306: recognize an S3CopyObjecterror embedded in an HTTP 200 response instead of reporting a successful copy. - Testify advances to v1.12.1 and the lint configuration adopts
gomodguard_v2. The Go 1.26 floor, Go 1.27.1 toolchain and go-systemd NetBSD replacement remain.
Component and migration boundary
The v3.14.0 password-policy change remains
in force; this patch does not revert it. Preserve the paired
admin:CreateUser / admin:ChangeMyPassword Deny where that was the intended
restriction, using the migration guide.
SILO 20260916, mcli 20260916, and Console 2.4.1 select this package; see the component matrix.
Release verification is recorded in the linked release and PR #9.
11 - SILO 20260916 Release Notes
Version date: 2026-09-16 · Published: 2026-09-16 UTC (September 17 in China) · Tag: RELEASE.2026-09-16T00-00-00Z · Package version: 20260916000000.0.0. Use the download page for installation.
The source range runs from released commit 9b11dc9469e6 to released 2a4d51406b7e: 210 commits, including 140 non-merge commits. These notes describe the final behavior, including the listing-quorum, version-purge and migration-tag repairs after #214.
Highlights
- Stronger request authentication: reject unsigned operation headers in ordinary and presigned SigV4, verify presigned body hashes, and use authenticated inputs for signature-age and payload-hash policies.
- Durable IAM revocation: retain deletion revisions, parent revocation boundaries and membership grant times across delayed events, same-name recreation and restart/recovery.
- One logical object across pools: coordinate conditional PUT, multipart completion, conditional DELETE, addressed-version deletion and metadata updates under consistent selection and locking rules.
- Preserve tags during pool migration: rebalance and decommission retain tags, their original revision and ordered empty states for ordinary and multipart objects.
- Correct encrypted and federated copies: preserve raw SSE-C replicas, plaintext part sizes, independently ordered lock/tag metadata, and the checksum, version and time of the actual destination write.
- More reliable replication and configuration convergence: repair tag resurrection, marker purge/MRF recovery, resync counters/cancellation, and bucket metadata source timestamps and deletions.
- Opt-in durable multipart discovery: add persistent scanning, global pagination and read-only preflight. Legacy remains the default; capacity and late-creation-write limitations remain explicit.
- Updated administration stack: select Console 2.4.1, mcli 20260916 and pkg 3.14.1, including bounded browsing, streaming ZIPs, reliable failure exits and policy correctness fixes.
- Two upgrade requirements need advance preparation: password permissions change the meaning of existing policies, and durable IAM revocation requires coordinated upgrades and complete backend backups.
Versions and components
| Component | Server 20260903 | Server 20260916 |
|---|---|---|
| Server | RELEASE.2026-09-03T13-18-01Z |
RELEASE.2026-09-16T00-00-00Z; inspected source 2a4d51406b7e |
| Embedded Console | Source 464a59d73ada, v2.3.0 identity |
Released v2.4.1, source 1360e26d976d |
| MC module / image-bundled mcli | 20260903 | Released 20260916, source e952aa78f10a |
| Shared package | github.com/pgsty/silo-pkg/v3 v3.13.2 |
v3.14.1, including the v3.13.3 and v3.14.0 changes |
| Upstream S3 SDK | minio-go/v7, revision 0e78d3f18efe |
v7.3.1-0.20260915093545-32e1f32cb176 |
| Go / madmin-go | Go 1.27.1 / v3.0.110 | Unchanged |
| Helm | 7.0.2 | Chart 7.0.3 in the tagged source; no separate chart package or Helm repository publication |
The release explicitly replaces Console and MC with v0.0.0-20260916075814-1360e26d976d and v0.0.0-20260916070421-e952aa78f10a. Go does not inherit a dependency’s replacements. The shared package uses the PGSTY module path directly; Server, Console and MC retain their historical module identities and protocol names. minio-go/v7 continues to use a verified upstream revision.
The supported release stack is SILO + Console + mcli + silo-pkg; compatibility with unmodified upstream MinIO/MC remains best effort.
Security and authorization
This cycle includes four local security identifiers. SN- identifiers are project tracking IDs, not CVEs. See the signed-header contract and September security notes.
| Identifier | Repair and scope | References |
|---|---|---|
| SN-2026-011 | Ordinary Authorization and presigned SigV4 reject unsigned x-amz-* operation headers, preventing a signed PUT from becoming a CopyObject with the signer’s authority. Empty-first-value multi-value headers cannot bypass the check. |
#173, #177 |
| SN-2026-012 | Generic authentication verifies presigned bodies whose hash is supplied only through a header; mismatches return XAmzContentSHA256Mismatch. The caller must hold the corresponding signed write URL. |
#177 |
| SN-2026-013 | Persistent IAM revisions and parent boundaries prevent older events, child credentials and grants from crossing retained revocation history. | #191, #192 |
| SN-2026-014 | Console’s anonymous sharing proxy accepts only object-content GETs at the configured S3 origin and rejects redirects, system paths and operation-selecting queries. | Console #56, Server #209 |
Related changes remove ambiguous duplicate replication-source headers, derive s3:signatureAge from the signed date rather than a scratch header, align presigned query/header payload-hash precedence, and inject the server-generated PutObjectTagging header after authentication but before authorization. X-Amz-Content-Sha256 may remain outside SignedHeaders because its effective value is separately bound and checked. Explicitly signed UNSIGNED-PAYLOAD retains its meaning.
Streaming SigV4 seed operation headers remain outside this check. Unsigned-header rejection returns 400 AccessDenied. The Console fix adds no global sharing-disable variable and continues to enforce S3 authorization.
Password permission split: a compatibility change
Server #175, pkg #6 and Console #51 separate password self-service from user administration:
- An internal user changing their own password checks
admin:ChangeMyPassword, allowed unless explicitly denied. - Creating users or resetting someone else’s password still requires
admin:CreateUser. Existing root, STS and service-account restrictions remain. - A standalone CreateUser Deny no longer locks the caller’s password. An existing ChangeMyPassword Deny now constrains this endpoint.
- Updated built-in
readonlyno longer overrides a separate CreateUser grant with its own deny; the package also supplies the separateconsolereadonlycapability policy. Saved policy overrides are not rewritten.
To retain the old combined restriction, deny both actions in the same statement before upgrading, preserving its resources and conditions. Keep both denies during rollback: an old Server cannot enforce a password-only deny on this endpoint. See password migration.
Shared policy correctness
pkg #4 fixes statement deduplication that omitted NotResource from hashing or collapsed distinct string-condition sets, potentially discarding Deny clauses. Directly constructed policies report Deny before indexing. Multi-star wildcard matching no longer uses exponential recursion while retaining byte matching and historical MatchSimple boundaries.
Requests that relied on lost denies may now be rejected. If a clause has already disappeared from stored JSON, upgrading cannot reconstruct it: compare with the original policy and reapply it.
IAM replication, revocation and recovery
Deletion revisions remain in the original IAM paths for users, service accounts, groups, policies and policy mappings. Normal listing and authorization hide deleted records. Object and etcd backends serialize revision comparison and persistence, retain source timestamps, and prefer deletion over a live record at the same timestamp.
- Delayed same-backend deletion notifications reload committed storage rather than deleting a subsequently recreated identity.
- Recreated parents retain
RevokedBefore; new service accounts and internal STS credentials carry a signedsiloParentRevocationboundary. Replaying or editing an old child does not renew its authority. - Each group member retains its own grant time. Editing another member or group status does not regrant every member.
- Newer service-account snapshots can replace an older copy of the same key while preserving secrets, disabled state, absolute expiration and revocation boundaries. Credential-type conflicts fail; expired snapshots cannot extend old credential life.
- Healing resumes after leadership is regained, retains acknowledgements and retries bounded batches. Natural expiration of ordinary STS credentials does not automatically create permanent deletion history; reusable service-account keys retain revisions.
- IAM metrics report retained revocations, healing failures, duration and last success. See the design and metrics.
Cross-site revocation propagates asynchronously. A parent deletion commits before secondary cleanup; if cleanup returns HTTP 500, the parent revocation is already in effect. Tombstones have no TTL or automatic compaction, and storage/startup costs grow with retained history. Concurrent independent membership edits, equal-time conflicting live updates, arbitrary clock skew and cross-site individual-token revocation retain documented limits.
S3 conditions and multi-pool consistency
The coordination layer selects a logical object under a shared namespace lock before evaluating conditions or merging copies of the same version. Relevant draining pools participate. Retention, legal hold and tags each use their own revision; object modification time is not a version for every metadata field.
| Operation | Change | References |
|---|---|---|
| Single-object DELETE | A nonempty If-Match compares the client-visible ETag before deletion and rejects a mismatch with 412. * requires an existing non-marker representation; explicit versionId compares that version. |
#145, #178 |
| Ordinary conditional PUT | If-Match/If-None-Match use the logical current object across pools, rather than a stale destination copy. A current delete marker means absence. | #207, fixing #199 |
| Conditional multipart completion | Evaluate once across pools under the object lock, not only beside the upload. | #190 |
| Ordinary addressed-version DELETE | Reconcile copies across pools, covering UUID/null versions, delete markers and applicable directory-marker deletion, preventing an older surviving copy from becoming visible under the supported quorum conditions. | #188 |
| Object and metadata mutation | Coordinate writes, metadata, healing, replication and movement while retaining independent lock/tag revisions and shared remote-tier references. | #178, #189 |
Availability tradeoff: unverifiable metadata in another relevant pool can fail conditional PUT/completion or coordinated deletion even when another pool serves GET/HEAD. Read-quorum failures return 503; restore readability before retrying. Cleanup can fail after partial physical mutation; there is no distributed rollback. Pending outbound delete replication retains versions until purge completes.
A public conditional PUT with a destination versionId compares the current object while preserving the requested write version. It does not clean stale copies, reverse previously accepted overwrites or provide global clock ordering. Multipart #190 and ordinary PUT #207 address separate defects.
Unsupported boundaries: batch DeleteObjects XML ETag still provides no condition protection. Empty If-Match follows ordinary deletion; internal recursive prefix deletion bypasses object conditions. There is no s3:if-match policy key or new GetObject authorization requirement. Use the supported single-object path when conditions matter. See conditional DELETE and multi-pool consistency.
Preserve object tags during pool migration
fced8630365f repairs lost object tags during rebalance/decommission. The read representation stores tags separately in ObjectInfo.UserTags; forwarding UserDefined alone could omit the canonical tag field needed by the destination write.
The four ordinary-PUT and multipart-initialization entry points now rebuild that field in an independent metadata copy. They preserve the original tag revision, including absent/empty revisions and ordered empty states, without generating a new revision or mutating the reader’s map. Writes and multipart completion retain existing rereads under the coordination lock so an old reader does not override a later tag update or removal.
The patch does not recover tags lost during earlier migrations. Audit previously migrated objects and tag-dependent policy and lifecycle rules separately.
Null-version listing quorum
8d06424b1 prevents a newer minority from hiding an older null version that has listing quorum. It recounts per header only when the original selection lacks quorum, every nonempty drive stream has exactly one ordinary null version, and all share the same erasure layout. Mixed version histories retain their previous behavior.
Remaining limitation: #218 tracks successful listings that still omit readable objects during rolling restarts with concurrent overwrites. Do not run synchronization tools that delete destination objects based on such listings; list again after the cluster stabilizes.
Encryption, checksums and object attributes
- Raw SSE-C replicas: store authorized replica ciphertext verbatim, avoiding another encryption/compression pass caused by destination defaults (#109, #122).
- Plaintext multipart sizes: preserve logical part lengths in replicas, repairing part-number/range reads, object size after replacement and attributes (#119, #124).
- No new compressed SSE-C objects: exclude ordinary PUT, multipart initialization, COPY and Snowball SSE-C writes from compression. SSE-S3/KMS retain their encryption-compression options (#118, #126).
- Retransmission repair: update existing or undecodable SSE-C replicas through object retransmission instead of ordinary metadata COPY, preserving newer destination retention/hold (#120, #134).
- Explicit checksums during key rotation: perform the required full rewrite, including requests for the existing algorithm. A multipart source may become a single-part destination and its ETag may change (#113, #123).
- Logical attributes: GetObjectAttributes returns plaintext part sizes for compressed/encrypted objects and terminates pagination correctly with sparse part numbers (#114, #115, #128).
- Honest unreadable-object errors: return 500 for integrity/decryption failures instead of a 206 accepted as a successful read (#110, #121).
- Chunked checksum interoperability: honor and validate the actual checksum header on non-trailer chunked PUTs even when x-amz-trailer is advertised (#107, #143).
These prevent new errors and provide supported repair paths; they do not rewrite historical encrypted data. Retain keys and exact version identities when auditing compressed SSE-C, wrong part sizes or unreadable replicas. See SSE-C integrity.
Federated CopyObject
This section concerns remote forwarding in legacy etcd bucket federation, rather than bucket/site replication. Changes accumulate through #157, #159, #163, #172, #177 and #179.
| Previous problem | Corrected behavior |
|---|---|
| Ignored checksum algorithm and incorrect empty/multipart results | Compute/forward logical full-object checksums with correct empty-body, trailer and inherited-checksum handling; reject missing, malformed or multipart-suffixed required remote results |
| Inline source rejected because internal metadata leaked | Strip the complete reserved internal metadata category case-insensitively; use public fields for metadata and tags |
| Wrong encrypted-source bytes/length or double transformation | Forward logical plaintext and length; let the destination apply explicit SSE or its defaults; send KMS context as a JSON object |
| Proxy auto-encryption overrode destination defaults | Leave the destination in control when no explicit SSE mode was requested |
| Legal hold lost or encoded as user metadata | Use actual Object Lock headers and retain full retention timestamp precision |
| Zero LastModified, missing destination version, empty event identity | Bind responses/events to the actual committed destination write, not proxy time or a later unversioned HEAD |
| Undefined raw SSE-C replica COPY | Return 501 NotImplemented before creating the destination; ordinary COPY with the source customer key is a separate supported operation |
Issues: #99, #100, #158, #166–#171. A remote write may have committed before response validation fails; inspect the actual destination version before retrying a versioned copy. See the federated copy contract.
Object Lock, tags and replication recovery
Independent lock and tag ordering
- An ordinary write supplying only legal hold no longer suppresses bucket default retention; ON and OFF do not substitute for retention selection, and explicit timestamp precision is retained (#165, #177).
- Empty-value and timestamp-only retention/hold removals participate in ordering, preserve newer destination state and no longer cause endless retransmission (#111, #117, #129, #131).
- Replica writes reconcile authoritative Object Lock state across pools, extending the earlier single-set boundary (#133, #178).
- Pool reconciliation carries tags with their revisions; SSE-KMS metadata COPY and SSE-C rotation retain tag timestamps. Local PUT/DELETE tagging advances revisions monotonically, and empty tags are ordered deletions rather than missing state (#189, #193, #196).
Both replication endpoints need the fixes to respect empty-tag tombstones. Explicit resync/heal adds a metadata COPY for objects with recorded tag revisions; destination default KMS can make that operation rewrite data. Tag-filtered rules still evaluate target eligibility using the post-deletion empty tags. Missing or malformed historical revisions are not reconstructed. See Object Lock ordering and replicated tag ordering.
Delete markers, MRF and resync
- Correct single-object marker-purge classification and retain marker identity/retry state through workers, scanner/heal and persistent MRF recovery. Accept a relevant 405 marker response only when bucket, key, version and modification time match. Normalize purge audit status from COMPLETE to COMPLETED (#153, #162, #196).
- Expose bounded MRF queue drops through administration, metrics and deduplicated logs, and align object/delete worker allocation. This does not turn the bounded queue into a lossless unbounded queue (#152).
- Persist final counters before publishing resync Completed; count success by the actual replication outcome rather than the existence of a target object (#136, #139).
- Dispatch only to the resync target ARN. Give each resync ID an independent cancellation context, drain blocked dispatch/walkers/workers, and prevent older work from overwriting terminal state (#141, #137).
Exact-version purges and queued creation
eb4f5e5b3 stops a pure version purge from creating markers on drives that lacked them. Missing-version retries require write-quorum absence; purge results combine removed and reliably absent replies. Receivers resolve the addressed version across pools, and marker healing preserves stored replication and purge metadata.
254b19ac0 rereads queued marker creations under the replication lock. A missing version, non-marker version or version under purge makes the task stale; an uncertain read is retried through MRF. The check covers tasks queued by the DELETE handler, GET/HEAD/LIST healing, scanner and MRF.
358ab38fb reports a delete marker only for a stored marker, preventing a data version awaiting purge from incorrectly returning x-amz-delete-marker: true and producing the wrong removal event.
Remaining limitations: creations already in flight, replays from another site, and minority residue after a crash following a majority-acknowledged purge remain tracked in #217. This is not a persistent receiver-side purge fence.
Replica metadata normalization
Restore only six replication-specific fields after ordinary metadata extraction, preventing transport-only aws-chunked from becoming stored Content-Encoding. Trusted Snowball entries no longer inherit ordinary metadata from the outer archive (#185, #194).
These changes do not scan/rewrite existing objects or recover lost tags, lock history or every unfinished purge. Use the read-only audit tool and runbook to inspect exact versions, mixed encodings, ambiguous findings and controlled recovery rather than running bulk self-COPY.
Bucket configuration, caches and startup
PR #180 completes source-state convergence for #77. Policy, Tags, SSE, Quota, Versioning and Object Lock read/compare/mutate/save under the same metadata.lock. Peer events retain source time; local changes advance monotonically past retained time. Equal-time deletions win, and stable content keys resolve other conflicts.
- Dedicated events, bulk, import, initial synchronization and heal use the same rules. Omitted fields leave existing settings unchanged; empty Versioning/Object Lock events still do not delete those settings.
- Canonical policy JSON allows accepted NotAction/NotResource policies to be retrieved/exported. Statement/set order may change without changing authorization.
- Empty Policy retains deletion semantics. Zero Quota remains a live document imposing no capacity limit, rather than silently becoming a different deletion state.
- Missing historical bucket Created values can be recovered from physical information and persisted. Adoption does not turn empty defaults into deletions, and unknown/invalid/unreachable sources receive bounded diagnostics.
MINIO_SITE_REPLICATION_METADATA_TOMBSTONESis off by default. Upgrade every participating site before enabling it consistently and restarting, exposing empty-payload Tags/SSE/Quota deletion times to heal/initial sync. Missed-deletion convergence remains limited while it is off.
Historical receive-time pollution is not automatically rewritten, and rolling downgrade across the new persistent IAM state remains unsupported. See bucket metadata convergence.
Other fixes close remaining bucket deletion/update/lifecycle/cache publication races, remove the reload guard that mistook the maximum field timestamp for a whole-record version, and preserve per-bucket CORS through metadata export/import (#105, #112, #127, #156, #164).
#116 adds startup-readiness documentation and Linux restart evidence. Verify readiness with signed PUT/GET requests at each coordinator in addition to health probes.
Multipart discovery, pagination and cancellation
PR #198 adds durable discovery; #213 restores conservative defaults and rollback compatibility. The final default in 20260916 is legacy. (SILO 20261018 4733e1423 subsequently retired MINIO_API_MULTIPART_LISTING=strict and kept legacy cache/exact-key listing as the sole public mode; see the latest decision.)
| Behavior | Default legacy | Explicit strict |
|---|---|---|
| Discovery | Released exact-key/node-cache behavior, retaining known defects | Scan durable uploads across pools/sets/drives and verify quorum |
| S3 semantics | No promise of repaired prefix, delimiter or complete pagination | Global prefix, delimiter, ordering and markers; continue after the returned marker’s upload completes or is canceled |
| Legacy uploads | Preserve existing availability | Diagnostic 503 for missing durable bucket/key identity; never fake a successful empty page |
| Abort | Existing read quorum, best-effort cleanup and pool ordering | Majority deletion acknowledgements in relevant sets; retry minority remnants and do not mask observed-remnant deletion failures with empty drives |
New writers record reserved bucket/key identity in the existing quorum-written xl.meta and remove upload-only fields on completion. There is no secondary index or directory migration. The shared maxUploadsList request limit decreases from 10,000 to 1,000 in both modes. This bounds the accepted page-size parameter; the legacy cache path can still return more entries because it ignores pagination.
Enable strict only through process environment MINIO_API_MULTIPART_LISTING=strict and restart; mcli admin config set is not supported. Invalid values diagnose and fall back to legacy while retaining other API settings. First upgrade every writer, drain legacy uploads, run the read-only GET /minio/admin/v3/multipart-preflight endpoint with admin:StorageInfo, and validate capacity. Unidentified legacy uploads can block strict lists for other buckets.
Each process admits two scans; each scan has 16 identity workers, four metadata workers, a 30-second context and a 100,000 aggregate directory-entry budget (counted across drives, so 100,000 directory entries rather than 100,000 unique uploads). Every page rescans persistent state. Exhaustion or uncertain coverage returns 503 rather than a successful partial page. A temporary 10,000-upload/four-APFS-volume experiment took 18.7 seconds for one page, missing the provisional five-second target.
Both modes fix wrong-key/bucket Abort evicting a valid upload’s cache and peer notification cancellation after unlocking. HTTP retains idempotent 204 for an absent upload. Late physical creation writes can still restore an upload after acknowledged cancellation; no durable creation fence was added. #79 therefore remains open. See the enablement and rollback contract.
Cumulative Console 2.4.1 improvements
Beyond sharing and password permissions, the embedded selection includes these post-20260903 changes:
- Cursor browsing: ListObjectsV2 pages of 50/100/250/500/1000 entries, with explicitly page-scoped filtering, sorting and selection; no full-bucket count scan. Failed page changes preserve the current listing, cancellation releases list workers, and rewind discloses row/time limits. Empty/null versioning responses no longer crash the list.
- Exact operation identity: retain bucket/key/version for deletion and constrain directories to their trailing-slash prefix. Lifecycle edits preserve current/noncurrent, date-based and unexposed settings, read legacy prefixes and remove stale filter representations. Replication-rule deletion retains shared targets and uses configuration deletion for the last rule. Governance bypass is explicit and off by default for authorized users, including object-only retention.
- Capabilities and sessions: gate Create Bucket on s3:CreateBucket; preserve sessions after background transport failures; URL-encode Watch filters and preserve structured Inspect errors without consuming bodies twice.
- Embedded compatibility: restore loopback TLS login, apply embedded local-proxy trust only to an explicitly initialized TCP loopback peer, retain standalone trust-none defaults, and forward all four WebSocket limits instead of incorrectly capping public browsing at eight connections per source (Server #108, #147, #148).
- Streaming ZIPs: use a backpressured, cancellable file writer where supported and native download management elsewhere, without buffering whole archives in JavaScript. Native handoff cancellation belongs to the browser. Show percentages only with reliable length and recommend mcli above 5 GiB or when size is unknown.
- Recovery/localization/accessibility: recover from malformed routes, persisted preferences and render errors, and complete English/Chinese labels, icon names, sign-out text and keyboard tooltips. Shared dropdowns no longer select a stale hovered item after a quick click/touch, or select another value when clicking a disabled item.
- Standalone packages: improve reproducibility, immutable tooling/browser inputs, SBOMs, signatures and provenance. Preserve minio-console.service, console-user and /etc/default/console; move certificates to /etc/silo-console/certs with explicit migration instructions. Installation does not automatically restart the service.
References: Console #47, #50, #57, 2.4.1 notes.
Cumulative mcli 20260916 improvements
The bundled client includes both 20260913 and 20260916 changes:
- Pipe honors applied global quiet/JSON settings, avoids progress frames on redirected output, preserves explicit checksums for empty input and streams nonempty input. MC_* booleans accept on/off/enabled/disabled.
- SQL/S3 Select, transfers, mirror permission errors, legalhold, partial recursive retention failures and mv source-deletion failures return nonzero. Mv waits for deletion results without rolling back successfully copied destinations.
- Per-object mirror permission errors do not stop later objects; finite failed jobs exit 1 and do not print an ordinary successful completion summary. Explicit JSON summaries report failure, and very short transfers retain finite speed values. Existing listing/watch cancellation-retry boundaries remain.
- Mirror –remove –watch preserves targets when historical source versions are deleted. Administrative dry runs never fall back to a real restart; non-TTY results remain reliable.
- Empty retention durations and invalid find –regex expressions report errors instead of panicking; JSON usage errors retain valid output contracts.
- The SDK detects CopyObject errors embedded in HTTP 200, preventing mv from deleting its source after false copy success. This applies when the client selects CopyObject, normally below 64 MiB or with –disable-multipart, while the default large-object path uses ComposeObject.
Scripts must inspect the final exit status rather than initial success messages or progress bytes. Skip-errors does not turn an observed failure into exit 0; some retention JSON failures may lack readable error text. Commands, aliases, MC_* variables, configuration format and package naming remain compatible.
HTTP, TLS and observability
- Absolute header deadline: #196 wires –read-header-timeout / MINIO_READ_HEADER_TIMEOUT into the server and prevents small reads from indefinitely extending HTTP/1 header parsing. The default is 30 seconds. Bodies retain rolling idle timeouts. Shorter values constrain TLS handshake reads; HTTP/2 retains its independent behavior. TLS writes and handler CPU/storage waits do not gain a new total deadline. Negative values disable the header-specific limit and are not recommended. The design responds to #183.
- Go TLS defaults: remove eight explicit Server curve overrides so listeners, nodes, identity providers, replication and etcd honor Go 1.27 defaults and applicable GODEBUG controls without weakening certificate/hostname checks (#164). This changes what SILO sends and accepts on the wire. With no GODEBUG set, the offered group list becomes Go’s current default, which additionally includes
SecP256r1MLKEM768andSecP384r1MLKEM1024; withGODEBUG=tlsmlkem=0set, that switch now actually takes effect — process-wide, and on the inbound listener as well as outbound connections. #154 is closed, though the reporter’s 2026-09-17 retest on this release still failed; total discovery/JWKS timeouts remain separate work. See TLS/OIDC and pinned TLS parameters and handshake compatibility. - CPU metrics race: locking around both map levels read by loadCPUMetrics prevents concurrent resource updates and Prometheus scrapes from terminating the process. The repair entered main through #214. Separate #215 closed as superseded, and issue #210 also closed.
- Quota/logger metrics: read the effective quota field, make AddSystemTarget idempotent and serialize logger initialization/console-target publication, avoiding duplicate collectors after log subscriptions (#106, #150, #132, #151).
Dependencies, delivery and maintenance
- Upgrade amqp091-go 1.10.0 to 1.14.0, including bounded malicious-frame allocation; SILO reachability requires an AMQP notification target (#176, #177).
- JWX 3.3.0 escapes custom JSON field names; strfmt 0.27.2 repairs Go 1.27 hostname validation; LZ4 4.1.30 fixes reader, partial-read and concurrency behavior, with Snowball/S3 Select integration regressions.
- Cumulative upstream SDK changes include configurable upload limits, streaming Content-Type signing, caller TLS trust for RDMA, consistent listing checksums/optional restore status, and the HTTP-200 CopyObject repair above. See the upstream adoption record. Console also updates React Router to 7.18.4 and retains js-yaml/smol-toml denial-of-service fixes with rebuilt embedded assets.
- Refresh Go x/* dependencies while retaining Go 1.27.1, the NetBSD go-systemd 22.6.0 replacement and tablewriter compatibility selection.
- Refresh container base digests and build static curl 8.22.0 from verified source on both Linux architectures. Pin published mcli 20260916 archives and SHA-256 values in the installer and image.
- Pin GoReleaser 2.18.1 and action v7.2.3 by full commit; dependency-only changes now trigger Test Release Pipeline. Include Helm Chart 7.0.3 in the tag with the new Server/client defaults; there is no separate chart publication.
- Fix conditional PUT and tag-replication test-fixture races, restore valid integration credentials, and add CPU/tag/multipart/S3 Select race coverage.
- Maintain rebrand guards, AGPL-3.0-or-later/DCO/no-CLA contribution rules and human contribution records; refresh README repository cards daily.
- Consolidate formal product, upgrade, security and design documentation on this site. Retain repository entry points and necessary compatibility/testing documents; move temporary investigations out of public source and retain historical evidence through immutable links.
September 4 Helm image-tag correction, mcli archive pinning and recovery container publication completed the previous release’s delivery.
Withdrawn experiment
GET-frequency-based cross-pool ILM tiering #60 was merged during this cycle and removed by #188. It never shipped in Server 20260903 and is not a feature of this release. The tracker, mover, scanner hooks, dedicated configuration/XML actions and metrics are removed. Ten old ILM keys are accepted but ignored; v8/v9 usage caches are readable and v8 is written without retired fields.
Intermediate development deployments should remove access-only rules and retain ordinary actions in mixed rules. Ordinary expiry, remote tiers, rebalance and decommission remain; independent multi-pool lock, conditional-delete, write-coordination and shared-tier protections remain. See migration.
Upgrade and recovery requirements
- Freeze final identities. Verify Server, Console, mcli, packages, images and Chart separately. Official image names are docker.io/pgsty/silo, docker.io/pgsty/mc and docker.io/pgsty/silo-console.
- Migrate password denies and compare original policies. Preserve CreateUser and ChangeMyPassword in the same Deny when the old combined restriction must remain. Recover previously lost clauses from original sources.
- Use coordinated maintenance. Pause IAM writes and isolate unknown offline sites. Upgrade every participating node/site, including nodes sharing IAM without site replication. Mixed old/new access to one IAM backend and rolling downgrade are unsupported.
- Save complete recovery points. Back up IAM storage and required encryption material; mcli admin cluster iam export omits deletion history. Stop relevant processes before restoration so cached peer acknowledgements are not reused.
- Verify every endpoint. Old revoked credentials must fail; new users, service accounts and newly issued STS credentials must work at every node. Check signed PUT/GET, bytes, versions, tags, retention and replication convergence before reopening traffic.
- Reconcile revocations after rollback. An old snapshot loses later revocations. Reconcile each restored site and test every endpoint; use differently named replacement identities/reissued credentials when needed. Pre-restore STS continuity is not promised. Do not delete tombstones online or convert only live records to force an old binary to start.
- Enable optional behaviors independently. Metadata tombstone export requires all participants upgraded. Strict multipart additionally requires all writers updated, legacy uploads drained, preflight and capacity validation. Ordinary upgrades may keep default legacy mode.
- Handle development-build remnants. If an intermediate build persisted api multipart_listing, back up configuration and use
mcli admin config reset ALIAS api multipart_listingto remove only that key, retaining other settings. Do not reset the entire subsystem or replay history containing the key. Follow the separate access-tiering retirement guide if that experiment was used. - Check TLS-inspecting devices on outbound paths. Handshake contents changed with the Go 1.27 toolchain in 20260903 and again here. An SSL-inspection NGFW, SASE gateway or IDS/IPS — or a JA3/JA4 client-fingerprint allow-list — can reset identity provider, webhook, remote tier or replication connections with
connection reset by peerwhilecurlfrom the same container succeeds. After upgrading, verify OIDC discovery and JWKS, audit and notification targets, remote tiers and replication endpoints. Remedies and their trade-offs: pinned TLS parameters and handshake compatibility. - Audit historical data separately. Inventory unreadable SSE-C, wrong part sizes, tags/locks, aws-chunked pollution, past erroneous overwrites and pending purges. Validate approved repairs on copies.
Runbooks: IAM upgrade/recovery, historical replica audit, password migration and multipart contract. While object and erasure layouts are unchanged, the new persistent IAM state prevents rolling downgrade to an older Server binary.
Known limitations and remaining work
| Item | Boundary / disposition |
|---|---|
| #79, multipart listing | Strict exists as an option; legacy remains default. Production capacity, durable late-creation fencing and full delivery acceptance remain open. |
| Batch conditional deletion | XML ETags provide no protection; empty headers, recursive extensions and policy enforcement are outside the implemented contract. |
| Streaming SigV4 | Seed operation-header coverage remains open beyond the ordinary and presigned SigV4 repairs. |
| IAM/configuration history | Lost revisions cannot be reconstructed automatically; clock, membership and retained-history costs remain. |
| #197, two-drive EC documentation | Open. |
| Other proposals | #20 Renovate, #30 Operator, #40 notification target hot reload, #61 KES/OpenBao planning and #160 image processing remain open outside this release. |
| Historical migration tags | Previously lost tags are not recovered automatically; audit tag-dependent policy and lifecycle rules separately. |
| #217 / #218 | In-flight/cross-site creation, crash residue and restart-time listing omissions remain unresolved; see the scopes above. |
Validation and release status
Released source 2a4d51406b7e passed Go CI and the Test Release Pipeline. The Release build and Finalize completed. The GitHub release provides 36 assets, including six-platform archives, Linux packages, SBOMs, checksums and attestations.
Console v2.4.1, mcli 20260916 and pkg v3.14.1 have independent published releases. At the 2026-09-17 recheck, anonymous token requests for standalone docker.io/pgsty/silo-console returned HTTP 401, so public pulls are not confirmed. Use Console’s GitHub binaries or packages. This standalone image limitation does not affect the source selected for Server embedding.
Issue index and acknowledgements
Fixed or completed within the described scopes: conditions #10/#133/#144/#199; federation #99/#100/#158/#165–#171; bucket metadata #77/#105/#112; encryption/attributes #107/#109–#115/#117–#120; embedded Console #108/#147/#148; replication recovery #136/#137/#139/#141/#152/#153/#185; runtime/metrics #106/#116/#150/#154/#176/#183/#210. The pool-migration tag repair is recorded as a direct main commit; #79 remains partially implemented and open. The sections above link the individual records and limits.
Thanks to reporters, patch authors and reviewers, particularly @mrjavadseydi (quota, multipart and tiering exploration), @nikitapogromsky (logger idempotency), @Aeirx (federated legal hold), @magicxor (early conditional DELETE proposal), @julienlau (purge/MRF), @chodorenko (replica encoding), @orenyomtov (signed headers), @jiri-pejchal (Console sharing), @AEGEGE (HTTP report), @aschyolkin (CPU race), and @vampywiz17, @kh0mka and other reproduction/feedback contributors.
Original PRs #12, #125, #155, #184, #187 and #195 were superseded by later implementations. See the full contribution record.
12 - mcli 20260916 Released
Release: RELEASE.2026-09-16T00-00-00Z · Package: 20260916000000.0.0 · Source: e952aa78f10a.
Changes since 20260913
- Adopt silo-pkg v3.14.1, upstream minio-go v7.3.1-0.20260915093545-32e1f32cb176, and JWX v3.3.0. CopyObject retries and propagates S3 errors embedded in HTTP 200 responses, so a failed copy cannot authorize
mvto delete its source. JWX correctly escapes JSON field names. - Report
mirrorpermission failures, including unreadable sources, rejected writes and failed local destination removals. Later objects continue; a finite mirror with failures exits 1. These per-object permission errors do not require--skip-errorsand do not restart an entire watch scan. - Preserve the existing cancellation/retry path for listing and watcher failures. Persistent listing permission denial can still cause repeated scans; this release does not redesign that retry policy.
- Suppress normal final success statistics after a failed mirror. Explicit
--summaryretains statistics: JSON usesstatus: failure; text error diagnostics remain on stderr. Transfers completed within one clock tick no longer produce infinite JSON speeds. - Return failure for legalhold set/clear errors, partial recursive retention failures, and moves whose source deletion fails.
mvwaits for deletion results; successful copies are not rolled back. Retention reports each failed object once instead of duplicate or misleading diagnostics. - Reject empty retention durations and invalid
find --regexexpressions with normal CLI errors instead of panics.
Automation and upgrade notes
Affected failure paths that returned 0 now return 1. Check final exit status and error records. For mv, a deletion failure may leave both the source and the successfully copied destination; inspect both before retrying.
Configuration format, aliases, MC_* variables, commands and package naming remain unchanged. The password-policy split introduced by pkg v3.14.0 is unchanged in v3.14.1. See the component matrix and password-policy migration.
SILO is the maintained administrative integration target. Compatibility with unmodified upstream MinIO and other S3 services remains best effort.
Delivery and validation
The release contains 19 assets: six Linux/macOS/Windows archives for amd64/arm64, six Linux RPM/DEB/APK packages, one archive checksum manifest and six package checksum sidecars. Archives, the manifest and DEB/APK packages have verified Sigstore build attestations bound to this signed tag and source commit. RPMs carry the PGSTY GPG signature (9592A7BC7A682E7333376E09E7935D8DB9BD8B20).
The final release commit passed Go tests on Linux, macOS and Windows with the race detector, Linux 386 tests, cross-compilation, vulnerability scanning and the release packaging pipeline. Pre-release acceptance also exercised both the published SILO 20260903 and fixed Server source 3c26a8b0b5bd, Object Lock and permission failures, CopyObject error injection, and both-architecture RPM/DEB/APK installation and upgrades. No reachable or imported-package vulnerability was found; an unused OpenPGP module-only advisory remains.
CI: VulnCheck · Test Release Pipeline · Crosscompile · Go · Release · Docker
The docker.io/pgsty/mc release and latest tags resolve to the verified Linux amd64/arm64 manifest:
Download and installation · Client compatibility · Changes since 20260913.
The HTTP-200 CopyObject repair applies when mcli selects a single CopyObject: objects smaller than 64 MiB or explicit --disable-multipart. Larger copies use ComposeObject.
See pkg 3.14.1 for the precise JWX/GHSA-4cf7-xm37-g63h scope. A failed --json mirror without --summary emits no trailing statistics record; --skip-errors does not turn recorded failures back into exit 0. Some retention JSON failure records carry status: failure with an error object serialized as {}; consumers must not require a human-readable message.
13 - mcli 20260913 Released
Release: RELEASE.2026-09-13T00-00-00Z
· Package: 20260913000000.0.0
· Source: 4f609a4d.
Changes since 20260903
- Preserve historical destination versions under
mirror --remove --watch. - Keep service-restart dry runs from executing a restart and make noninteractive behavior explicit.
- Return failure for transfer and S3 Select errors, honor explicit checksums on
empty uploads, and retain
pipeJSON output when quiet mode is enabled. - Accept on/off boolean environment values and repair CLI/JSON behavior across supported platforms.
- Adopt pkg v3.14.0 and upstream SDK
v7.3.1-0.20260910142817-60bd07042d49, refresh Go x/* and UBI dependencies, and keep Go 1.27.1. NetBSD retains the go-systemd v22.6.0 portability pin.
The package retains the policy Deny/NotResource and bounded wildcard repairs from pkg v3.13.3. Correctly preserved denies may now reject requests that relied on lost clauses. Recover already-lost clauses from the original policy source.
The new pkg also changes password capabilities. Read the password-policy migration before a coordinated Server upgrade. The matching password UI shipped in Console v2.4.1; see the component matrix for the coordinated upgrade.
Delivery and validation
The immutable release contains 19 assets: six archives for Linux, macOS and Windows on amd64/arm64, RPM/DEB/APK packages for both Linux architectures, checksums and package checksum sidecars. Archives and the checksum manifest have verified build attestations; RPMs carry the PGSTY GPG signature. Native macOS arm64 version and Go build metadata were checked against the released pins.
The release and latest tags of docker.io/pgsty/mc resolve to the same verified
amd64/arm64 manifest:
Container publication completed; both architecture images were pulled and their version output checked. Go, cross-compilation, vulnerability and Test Release gates passed before tagging. No reachable or imported vulnerable package was reported; unused OpenPGP retains module-only GO-2026-5932. Compatibility with upstream MinIO and other S3 endpoints is best effort; SILO is the maintained administrative integration target.
Download and installation · Compatibility notes · Complete source diff.
pipe streams nonempty stdin until EOF; empty input uses a regular zero-byte PUT instead of unknown-size multipart. Explicit checksum options reach the upload path. Quiet/non-TTY execution keeps progress disabled while retaining JSON results for both empty and non-empty inputs.
14 - silo-pkg 3.14.0 Released
v3.14.0 was published on 2026-09-13 from commit 827f8109 under the module path github.com/pgsty/silo-pkg/v3.
Password-policy migration
This release updates authorization semantics for self-service password changes:
Policy.IsAllowedActionsnow allowsadmin:ChangeMyPasswordby default unless explicitly denied, whileadmin:CreateUserstill requires an explicitAllow. On matching Server and Console builds, denyingadmin:CreateUseralone no longer blocks a caller from changing their own password; denyingadmin:ChangeMyPassworddoes.- The built-in
readonlypolicy drops its previousadmin:CreateUserDenyrule (so an explicitadmin:CreateUserAllowgranted by another attached policy can now take effect). The newconsolereadonlypolicy adds bucket listing and uses the same permission split. Neither built-in policy grants user-management permissions on its own. - Existing custom policies and user-level policy overrides stored in the cluster are not rewritten automatically.
To keep the previous restriction that blocks both user administration and self-service password changes, include both admin:CreateUser and admin:ChangeMyPassword in the same Deny statement (preserving its original resource scope and conditions) before upgrading and throughout your rollback window—older Server builds do not enforce a standalone admin:ChangeMyPassword deny on this endpoint. See the full password-permission migration guide.
September 16 follow-up: Console v2.4.1 ships the matching password UI alongside silo-pkg v3.14.1 and mcli 20260916. See the component version matrix for coordinated upgrade requirements.
Dependencies and verification
- Upstream
minio-goupdate: Pinned tov7.3.1-0.20260910142817-60bd07042d49, bringing configurable upload size limits, streamingContent-Typesigning, caller TLS trust propagation for RDMA, consistent listing checksums, and optional archive-restore status parsing fixes. - Toolchain and dependency refresh: Refreshed
golang.org/x/*modules while keeping the Go 1.26 minimum requirement, Go 1.27.1 build toolchain, and existing exported Go signatures unchanged. Thego-systemd v22.6.0replacement is retained for NetBSD cross-compilation compatibility. - Testing and vulnerability scanning: Passed full
-racetest suites on Go 1.26.8 and 1.27.1, lint checks, LDAP configuration validation, and Go module proxy / checksum database resolution. Scanning withgovulncheck 1.8.0found no imported vulnerable packages (only the module-levelGO-2026-5932entry for unused OpenPGP subpackages remains).
This release retains the Deny/NotResource policy serialization fix and bounded wildcard matcher from v3.13.3 (policies that already lost statements under earlier versions must still be restored from their original source). The github.com/pgsty/silo-pkg/v3 module-path migration introduced in v3.13.0 is complete across all four maintained components.
15 - SILO Console 2.4.0 Released
v2.4.0 was published
on 2026-09-08 from c103d08e.
Released behavior
- Object-browser directories load 100 entries per page by default, with choices of 50, 100, 250, 500 and 1000. Each page uses one S3 continuation-token request. Sorting, filtering and selection apply to the displayed page, not a complete directory scan; there is no unbounded “all” mode.
- Version deletion stays bound to the selected object and directory deletion to its prefix. Lifecycle edits preserve independent actions and unexposed settings; replication rule deletion is saved consistently.
- Transient network failures preserve sessions. Empty versioning state, malformed sidebar preferences and stale dropdown selections are handled.
- Embedded Console restores loopback proxy behavior when Server explicitly enables the policy; standalone trust defaults remain unchanged.
- Dependencies include pkg v3.13.3, MC source
v0.0.0-20260908140805-c8aa5d25a63a, and upstream SDK0e78d3f18efe. This release consumed MC as source and did not itself publish a new mcli tag.
Correctly preserved Deny/NotResource clauses may reject requests that relied on old deduplication defects. Restore already-lost clauses from the original policy source. Embedders must copy the MC replacement from the tagged README.
Later release
Console v2.4.1 adds restricted shared downloads, the password-permission split, streaming multi-object ZIPs, browser recovery and verifiable release artifacts. It selects pkg v3.14.1 and mcli 20260916. v2.4.0 uses the earlier browser-memory ZIP path. Read the password migration before upgrading.
The exact v2.4.0 source passed the complete Console CI matrix. See the tagged changelog, object-browser guide, and release assets.
16 - Silo Console 2.2.0 Release Notes
Published: 2026-08-26 · Version: v2.2.0 (7dc4258a6) · Repository: pgsty/silo-console
SILO Console 2.2.0 adds a bounded text preview for logs and structured text, tightens download error handling so truncated archives cannot pass as complete, fixes table-action permission gating, splits user status updates from group membership edits, and validates database notification DSNs and IAM policy documents before writing them to the server.
Underneath, the dependency stack moves to Go 1.27.0 and onto the maintained SILO packages: pgsty/silo-pkg 3.12.1 replaces upstream minio/pkg, pgsty/mc replaces upstream mc as a library, minio-go moves to 7.3.0, and the etcd client line moves to 3.7.1, closing CVE-2026-73500.
The final change set since v2.1.1 spans 33 commits touching 176 source files (+8,794/−2,057 lines, excluding regenerated embedded frontend assets).
Why 2.2.0 instead of 2.1.2: this release switches the shared policy/certificate package provider, crosses an etcd client minor version boundary, splits a REST route, and changes folder-download failure semantics.
Text Preview for Logs and Structured Text
The object browser can now preview .log, .txt, .json, and .xml objects as literal text. The implementation is deliberately paranoid, because “render arbitrary bucket content in the admin UI” is an XSS invitation:
- Bounded by construction. The request carries
Range: bytes=0-1048576; the response is streamed into a fixed buffer with a hard cap, andContent-Range/Content-Lengthare strictly validated against what was actually read. An object over 1 MiB reports too large — it is never partially rendered as if complete. - Text or nothing. The bytes must decode as strict UTF-8 (
TextDecoderwithfatal: true) and must not contain NUL; anything else reports not previewable instead of rendering mojibake or binary junk. Eligibility is an exact allowlist — the four extensions plus the precise MIME typestext/plain,application/json,application/xml,text/xml— and.html/.htm/.xhtmlare explicitly excluded even when their metadata claimstext/plain. - No active document. Content renders into a single DOM text node — never
innerHTML, never an iframe, never a JSON-to-HTML transform. A regression suite feeds it HTML, SVG, and XML payloads and asserts they stay inert text. - Race-proof. Switching quickly between objects invalidates in-flight previews by generation, so a slow response for the previous object cannot paint over the current one. Cancellation and retry are first-class states.
- Anonymous-friendly. Public/anonymous object pages get the same preview through the anonymous request path, without triggering any credentialed API call.
Alongside the new preview type, the existing preview plumbing was corrected: empty objects preview as empty instead of erroring, appended logs re-preview at their new length instead of the stale listed size, and metadata races when flipping between objects are gone. The preview and share dialogs now receive the object’s real size (92e8f4e65).
Download Integrity and Progress
The download pipeline — progress reporting, single objects, folders, archives, and byte ranges — enforces strict completion and error reporting.
Indeterminate progress when size is unknown
A missing, invalid, or contradictory total no longer becomes a fabricated percentage: the progress indicator stays indeterminate until the real total is known. Zero-byte objects are normalized deliberately instead of falling into the unknown-total path, and abort/cancel are terminal states — a late progress event cannot resurrect a cancelled download. Download requests settle exactly once, JSON error blobs are parsed safely, and generated object URLs are revoked.
Streaming folder downloads
Single-folder downloads now use the browser’s native streaming download path instead of accumulating the entire ZIP in JavaScript memory — a multi-GB folder no longer risks tab death. One consequence of the handoff model: the console’s transfer manager reports the folder download complete once it hands the stream to the browser (a toast says so), and the browser’s own download UI takes over from there — cancelling in the console after handoff does not stop the browser-side transfer. Multi-selection downloads still ride the existing POST response and therefore remain an in-memory Blob; changing that requires a separate API decision and is out of scope here.
ZIP integrity — a deliberate behavior change
This is the one change most likely to be noticed as “downloads broke”. In 2.1.1 and earlier, a folder/ZIP download that failed to read some objects silently skipped them and delivered an HTTP 200 archive missing files. In 2.2.0 any per-object failure — list, stat, read, entry creation, close, or copy — aborts the archive: a clean error if nothing was sent yet, an aborted connection mid-stream, so a truncated ZIP can never pass for a complete one.
The practical consequence: a user whose policy grants List on a prefix but GetObject on only a subset of it — a common IAM setup — previously received a partial archive; now the download fails at the first denied object. That was silent data omission, and 2.2.0 treats it as the bug. Download exactly what you can read, or scope the folder download to a readable prefix.
Byte ranges and status codes
- Malformed or unsatisfiable non-empty
Rangeheaders now return416withContent-Range: bytes */Ninstead of a 500. Range parsing is stricter than Go’s lenient default: signs, embedded whitespace,bytes=-0, and empty range elements are rejected. - A range request against a zero-byte object returns an empty 200 instead of a 500 — this was the empty-object preview bug.
- Failures from the lazy object
Statnow surface the real S3 status (403, 404, …) instead of a blanket 500. 206responses now setContent-Lengthbefore the header flush, so partial responses carry correct framing.- Object sizes are now always serialized — REST and WebSocket listings report
"size": 0for zero-byte objects instead of omitting the field, and the UI displays0 B. This is the ground truth the honest-progress work stands on.
Version history
S3’s valid null version ID stays visible instead of being dropped, version counting filters prefix matches to the exact object, and history is retained after bucket versioning is suspended or disabled.
Permission Gates That Actually Gate
Row actions were never disabled
Every screen with a data table passed its permission predicate through a prop name (disableButtonFunction) that the table component no longer reads — so view/edit/delete buttons rendered enabled regardless of permission, on every one of the 10 affected screens (Users, Groups, Policies, IDP, webhook settings, bucket access/replication/lifecycle panels). The predicates are now wired to the prop the component actually honors, and a source-guard test fails the build if the dead prop name ever reappears. Server-side authorization was never affected — the buttons produced errors when clicked — but the UI now communicates permissions instead of lying about them.
Independent service-account capabilities
Access-key management previously keyed several UI decisions off one combined permission check. 2.2.0 derives four independent capabilities — List, Create, Update, Remove — in one module and applies them consistently across the self-service Account screen and the admin user-details screen:
- the service-accounts tab shows when you can list, the create button when you can create, row selection and bulk delete only when you can remove, and the edit pencil only when you can update;
- View is now genuinely read-only: all fields disabled, no submit path, Enter is inert — previously “view” opened the editable dialog;
- the session’s advertised Create Access Key capability is computed correctly for request-scoped policies: a
Denyconditioned onsvc:DurationSeconds(an expiry restriction that can only be evaluated once a concrete request exists) keeps the capability visible — including with wildcard admin actions — while unconditional or login-time denies now properly hide it. The server remains the final authority at request time. The previous code kept the capability visible for any conditional deny, which over-advertised it. - an OIDC create/list/get/delete integration regression now runs through the UI’s explicit-credential endpoint, including the expected self-update denial.
Validate IAM policies before writes
Named-policy and service-account policy writes now reject malformed documents and bare S3 resource ARNs with a client error before making an Admin API request. Historical policy reads stay permissive for compatibility, but an incompatible stored policy must be corrected before it can be saved again. The strict parser and resource checks live in the console’s write path instead of importing fork-only policy APIs, preserving the advertised upstream minio/pkg v3.6.1 source-build floor.
Anonymous pages behave anonymously
Anonymous object-browser pages no longer issue protected Object Lock/retention requests that could only produce Access Denied noise, and they gained the language (文/A) and dark-mode controls.
User Status and Groups Are Separate Operations
Toggling a user’s enabled/disabled status and editing their group membership were one combined PUT /user/{name} call that required both payloads and — through the combined permission gate — demanded admin:EnableUser merely to edit groups. 2.2.0 splits them:
PUT /user/{name}/status(new) changes only the status, validates the value againstenabled|disabled, and rejects the signed-in user’s attempt to enable/disable themselves. Failed toggles no longer leave the switch out of sync — the UI reflects the server’s answer, not an optimistic flip.PUT /user/{name}/groups(existing) is now the only thing group editing calls, and no longer requiresadmin:EnableUser.PUT /user/{name}survives unchanged on the wire as a deprecated compatibility endpoint for existing API consumers; an unknown status there is now a 400 instead of a 500.
See Compatibility for the API-contract details, including a caveat for regenerated Swagger clients.
Database Notification Forms That Emit What They Mean
The PostgreSQL and MySQL event-destination forms were rewritten around a shared DSN parser/serializer:
- Structured fields and the raw connection string are one state: the raw string stays authoritative until a structured field is edited, at which point a canonical DSN is rebuilt — libpq keyword/value quoting for PostgreSQL, IPv6-bracket-aware
go-sql-driverformat for MySQL. Mode switches no longer mangle manually entered strings. - Generated previews mask credentials; the mask can never reach the API payload (the payload always uses the raw connection value).
- Saving requires both a connection string and a table, and cleared values actually propagate.
- The server now rejects DSNs its own configuration grammar would corrupt — embedded newlines, values that would parse as sibling config keys (
table=…inside a password), unbalanced quoting — with a 400 before anything is stored, and without echoing the submitted secret back. - Generic password and token fields across notification targets (Kafka, Redis, MQTT, NATS, webhooks) render as password inputs, including environment-overridden values.
Accurate restart-required status
Configuration add, update, delete, and reset now honor the server’s actual restart-required answer instead of hardcoding “restart needed” (or worse, dropping it). The pending-restart flag is monotonic: once any operation requires a restart, later operations that don’t cannot clear it — only an actual service restart does.
Upload advisory
Browser uploads larger than 5 GiB get a non-blocking warning: the console uploads as one non-resumable request, and mcli with multipart upload is the right tool at that size.
Sessions, Metrics Auth, and i18n Hardening
- Sessions fail fast. An anonymous/empty session gets an immediate 401 instead of hanging on an empty-credential admin request. The console accepts the canonical
401and the legacy403invalid-session responses, and expired-session redirects are subpath-aware — a console served under/console/redirects within its base path. - Prometheus Basic auth works everywhere. The health check and the root-fallback probe now send Basic credentials (previously Bearer-only, so a Basic-auth Prometheus disabled every dashboard widget), Bearer tokens keep precedence, and every response body is drained so keep-alive connections are actually reused.
- Placeholder substitution is escape-proof by construction. Translated placeholder filling uses a one-pass literal formatter: values containing
$&,$1,$`, backticks, or braces stay literal, repeated placeholders all fill, and an AST source-guard test bans the unsafeString.replacepattern from ever returning.
Toolchain and Dependencies
Go 1.27 baseline
| Component | 2.1.1 | 2.2.0 |
|---|---|---|
go directive, build image, CI matrix |
1.26.5 / 1.26.x |
1.27.0 / 1.27.x |
golang.org/x/crypto |
v0.54.0 | v0.55.0 |
golang.org/x/net |
v0.57.0 | v0.58.0 |
golang.org/x/text |
v0.40.0 | v0.41.0 |
golang.org/x/mod |
v0.37.0 | v0.40.0 (closes CVE-2026-56864/-56865) |
golang.org/x/tools |
v0.47.0 | v0.49.0 |
The SILO forks
silo-pkg3.12.1 is the release dependency.pgsty/silo-pkgv3.12.1 was published on 2026-08-25 and the console pins it directly. It builds on the 3.12.0 release, which carries the policy resource-boundary hardening, condition-key lookup repair, LDAP and certificate-watcher fixes, and the Go 1.27 / etcd 3.7 baseline.- The
mclibrary moved to thepgsty/mcfork at a date-tagged pseudo-version. Import paths are unchanged. - The
requireline stays on upstreamv3.6.1deliberately. Go ignoresreplacedirectives in dependency modules, so a downstream module that requires this console resolves upstreamminio/pkg— and the SILOv3.12.1tag does not exist upstream. Requiring a real upstream tag keeps the console resolvable downstream; the replace applies the fork for the console’s own builds. The final CI verifies the public source-build surface against upstream v3.6.1. This is compile compatibility: a downstream build without its own top-level replace gets upstream behavior, not the fork’s SILO-specific IAM semantics. go-systemdis pinned back to v22.6.0: v22.7.0 usesCLOCK_MONOTONICon NetBSD, which doesn’t compile there; the pin holds until upstream ships the fix.
etcd 3.7.1 — client libraries only
All three etcd Go modules move together from 3.6.8 to 3.7.1, closing the TLS-listener denial of service GO-2026-6107 / CVE-2026-73500. etcd 3.7 removes legacy protobuf remnants and makes clientv3.New non-blocking — migrations that matter to consumers that construct clients or embed servers. The console does neither: its only etcd path is silo-pkg/quick operating on an already-created client with plain v3 Get/Put. This upgrades compiled client libraries only — it does not touch an operator’s etcd servers, cluster data, or deployment topology; a server upgrade to 3.7 still follows etcd’s own one-minor-at-a-time procedure.
Third-party maintenance stays conservative
The final release updates minio-go to v7.3.0 after a separate compatibility review, migrates its INI import path, and adds lifecycle-filter XML coverage. A console-side compatibility decoder accepts legacy AccountInfo tag payloads returned by older servers. Other accepted maintenance moves include jwx v2→v3 with httprc v3, go-openapi/swag/conv+typeutils 0.28.0, grpc-gateway 2.29.0, cheggaaa/pb 1.0.30, and go.yaml.in/yaml/v3 3.0.5. Larger unrelated go-openapi, pb/v3, compression, and test-library updates remain deferred.
On the frontend, the vulnerability workflow now covers pushes, manual runs, and development dependencies with immutable installs; vulnerable transitive resolutions were refreshed (fast-xml-parser 5.11, nanoid 3.3.18, @babel/core 7.29.7), and dead exports along with the unused http-status-codes dependency were removed. The fast-xml-parser 5.x transitive packages are development-only and are not bundled into the browser build.
Security Review
govulncheckat the release tree: zero vulnerable symbols reached, zero vulnerable imported packages. The Swagger build tool scans clean separately.- Closed by dependency moves: CVE-2026-73500 (etcd TLS listener DoS), CVE-2026-56864 / CVE-2026-56865 (x/mod verification).
- Still reported, still unreachable: the module-level GO-2026-5932 openpgp advisory — the console does not import
x/crypto/openpgp, and no fixed release exists. - The text preview was reviewed as an XSS surface (see above); its regression suite includes active-payload tests.
- The permission-gating fixes are UI-truthfulness fixes: server-side authorization was never bypassed in 2.1.1; the console simply displayed controls it shouldn’t have.
Compatibility
Nothing changes for deployment plumbing: no environment variable, configuration format, command, binary name, systemd unit, port, or embedded data layout changes. The release binary remains self-contained; Go 1.27.0 is a build-time requirement only.
HTTP API contract (console’s own REST API):
| Route | Change |
|---|---|
PUT /user/{name} |
Unchanged on the wire; now deprecated. OperationId renamed UpdateUserInfo → UpdateUserInfoLegacy, body model renamed to legacyUpdateUser (identical schema). Unknown status: 500 → 400. |
PUT /user/{name}/status |
New. Status-only body (enabled/disabled enum, 422 on violation), returns a user object populated with access key and status only — clients must not read group data from it. |
updateUser model |
Now status-only with enum — breaking for generated-spec consumers; the old shape lives on as legacyUpdateUser. |
| Object download | Error semantics changed: bad ranges 500→416 (+Content-Range: bytes */N), range-on-empty 500→200, Stat failures 500→real S3 status, ZIP failures silent-partial-200→visible failure, 206 responses carry Content-Length. |
| Listings & WebSocket | size always serialized, including 0. Additive. |
PUT /configs |
New 400 class for database DSNs the server’s config grammar would corrupt. |
| Config reset/delete | restart in the response now reflects the server’s real answer instead of always true. |
GET /session |
401 for empty-credential principals; the advertised Create-Access-Key capability is computed more strictly (see service accounts). |
If you generate client SDKs from swagger.yml: the UpdateUserInfo operation now points at /user/{name}/status with a status-only body. Code calling the generated UpdateUserInfo symbol keeps compiling but targets the new route; the old combined call is UpdateUserInfoLegacy. Audit call sites when you regenerate.
Behavior changes an operator may notice:
- Folder/ZIP downloads over partially readable prefixes fail instead of silently omitting unreadable objects (details).
- Range parsing is stricter than Go’s lenient default; degenerate range headers (
bytes=-0, empty elements) now get 416 instead of best-effort handling. - Database notification configs that only worked by accident (DSNs the config grammar mangled on the way in) are now rejected up front with a 400.
- The Create-Access-Key control is hidden for sessions whose policy unconditionally denies it (previously any conditional deny kept it visible).
- A pending restart-required indicator persists until an actual restart, instead of being clearable by a later unrelated config change.
- If a reverse proxy in front of the console compresses
/api/v1/…/downloadresponses, the text preview’s strictContent-Lengthverification will reject every preview as an error. The console itself only compresses static assets — leave API responses uncompressed at the proxy.
Known Limitations and Follow-up Items
Wiring the table disable prop exposed two pre-existing UI-guard defects where predicates compare a row object against a string:
- the “cannot delete the Default IDP configuration” row guard compares the row object against
"Default"and therefore does not disable the button (IDPConfigurations.tsx); - the “cannot delete an env-override webhook endpoint” row guard has the same object-vs-string mismatch (
WebhookSettings.tsx).
In both cases the server still enforces the restriction when the action is submitted. Additionally, the PostgreSQL DSN parser populates structured fields only from canonical key=value syntax (non-standard libpq DSNs display empty structured fields and rebuild from those fields if edited), and TestCafe/Playwright suites cover UI gating while live-server deny paths are covered by the integration suites.
Verification
The release decision combines local release-preparation evidence with remote gates run against the exact tagged tree, 7dc4258a6:
Local release-preparation gates — go build ./..., go vet ./... (plus -tags testrunmain), gofmt, golangci-lint, go test -race ./..., go tool swagger validate, govulncheck, TypeScript tsc, Playwright, Prettier, knip, release-tag cross-compiles for linux/amd64 and linux/arm64, and go mod verify.
Embedded-assets determinism — the frontend was rebuilt from source through the full pipeline (yarn build + embed optimization) and the result compared against the committed web-app/build: byte-identical, zero dirty files. The nine commits after the earlier 19047161f candidate changed Go compatibility, workflows, and browser-test timing, but not product frontend source or embedded assets.
CI, exact final tree — Workflow run 32888892876 reported 32 successful jobs and one explicitly disabled React-test placeholder. It covered lint, semgrep, Go and API tests, five cross-compile targets, Swagger drift, latest-MinIO source builds, distributed integration, site replication, hermetic SSO, the complete TestCafe permissions matrix, subpath-nginx, Playwright, and coverage. Vulnerability Check 32888899120 passed both jobs on the same commit.
Release pipeline — goreleaser run 32916237254 passed both jobs against v2.2.0 and published the public GitHub release.
Downstream contract — in a scratch tree with every replace removed, go mod tidy + go build ./... succeed against upstream minio/pkg v3.6.1, proving the fork replacement never leaks fork-only symbols into the public module surface.
Test-infrastructure repairs shipped in this cycle (so the gates above actually gate): the Docker-backed integration/replication/SSO suites are back behind the testrunmain build tag (a bare go test ./... no longer tries to start containers); the SSO gate is hermetic — no sudo /etc/hosts edits, no ad-hoc pip installs, pinned SILO image, own port, real teardown; the integration gate asserts the new 416 range semantics and stopped binding its PostgreSQL fixture to a host port; the browser gates run off-Linux by publishing fixture ports; a stale pre-fork “MinIO administrator” selector was fixed; state-mutating TestCafe suites are serialized; Playwright CI installs the committed lockfile immutably.
Release Artifacts
The v2.2.0 GitHub release publishes:
- six standalone binaries: Linux amd64/arm64/armv6, macOS amd64/arm64, and Windows amd64;
- nine Linux packages: DEB, RPM, and APK for amd64, arm64, and armv6;
silo-console_2.2.0_checksums.txt, plus the SHA-256 digest GitHub records for every asset.
Related Commits
16960f7ab— fix: keep unknown downloads indeterminate5968bb37d— chore(deps): align the SILO Go dependency stack288ab1240— fix: harden sessions, metrics, and translationsecf3bb492— fix: harden object previews and downloads902d9650d— chore: tighten dependency and test gates194c70c7a— build: prepare SILO Console v2.2.0927b44e26— fix: harden database notification formsda2191be9— fix: clarify console upload and secret limits6141c2445— build: refresh SILO Console v2.2.0 assets097e76155— chore(deps): bump the shared package fork to v3.12.099ca523d6— fix: split user status updates out of the combined user routef4097992f— fix: honor the server restart result for configuration changesf1280032a— fix: restore permission-gated table row actions24ce0af97— fix: keep request-scoped access key conditions visible in the console92e8f4e65— fix: pass the object size into the preview and share dialogs8f6fb3c78— test: make the SSO gate hermetic and pin it to a SILO release384a2cb95— build: refresh SILO Console assets and record the changesa73cda376— test: fix the integration gate’s stale range and host portcf5049c1d— test: make the browser gates runnable and fix a stale selector6fa19d857— fix: complete service account permission boundaries7e57771a4— build: refresh SILO Console assets57cfe7aa0— fix: restore downstream and browser release gates19047161f— test: stabilize permissions browser gatese37dec873— fix: validate IAM policies before writes28505ed23— chore: update minio-go to v7.3.016abb971e— ci: harden validation and release gates2ddfcd036— fix: accept legacy AccountInfo tag payloads31332bca9— fix: preserve policy source compatibility3a8251086— ci: allow permission tests to finishc159fff78— ci: serialize shared-role permission tests2e91cdf9a— test: wait for watch controls to become ready7dc4258a6— test: allow asynchronous UI controls to settle
Links:
17 - SILO Console 2.5: Interface and Administration Updates
SILO Console v2.5.1 is now released (commit fff1618d9abb) and ships embedded inside SILO 20261026. This note summarizes the full 2.5 release series since v2.4.2, combining the v2.5.0 interface overhaul and the v2.5.1 patch fixes.
GitHub release · Downloads · Full commit comparison
Navigation, overview, and metrics
The 2.5 interface adopts neutral light and dark themes, system typography, consistent information density, Lucide icons, clear keyboard focus states, and prefers-reduced-motion support. A mobile drawer and tablet rail adapt navigation to narrow viewports while respecting saved user preferences; object details open in a focus-restoring bottom sheet on mobile, and dialogs and forms scale cleanly to smaller screens. English and Chinese localization, accessible controls, and license attributions remain built in.
Permission-aware navigation organizes Identity, Monitoring, Settings, and Tools around the active session’s grants, consolidates bucket access under a single Buckets entry with recent-bucket shortcuts, and preserves post-login redirect targets and existing object deep links (/browser redirects to /buckets, and bare bucket-management URLs open the Summary tab). Help continues to link directly to Documentation, Video guides, and Blog/RSS feeds.
The Overview dashboard displays node and drive health, raw physical capacity, active alerts, quick actions, and data freshness. Capacity panels report the exact figures returned by Server rather than estimating usable erasure-coded capacity, marking uncollected statistics as unknown. Metrics V3 panels under Monitoring distinguish missing Prometheus samples from true zero values and handle per-node cluster metric exports accurately. Note that while Server 20261026 returns a live bucket inventory, bucket and account usage totals remain scanner-derived.
Buckets, object browser, and tasks
- Bucket list: Supports sorting, name filtering, URL query-parameter persistence, table/card view toggling, large-list virtualization, and explicit unknown-usage badges. Stable row keys keep selection attached to the right bucket across sorts, and in-flight search updates are discarded on navigation.
- Fine-grained permission handling: Write-only users can enter the object workspace; wildcard bucket-root grants and prefix restrictions are preserved; and rejected actions (such as tag edits) surface the server’s error message directly (listing, read, and mutation permissions remain evaluated independently).
- Bounded previews and CLI hints: Adds bounded 1 MiB previews for plain text, Markdown, and CSV/TSV files alongside source views, sanitized HTML rendering, and explicit truncation notices. Supported configuration forms also display secret-redacted
mclicommand equivalents. - Tasks center: Tracks uploads, downloads, native streaming ZIP downloads, bulk deletes, noncurrent-version cleanup, and object restores with bounded mutation concurrency and failed-item-only retries (canceling a task does not roll back already-completed writes; verify large object or streaming ZIP downloads on disk after browser transfer completes).
- Keyboard navigation and pagination: Adds arrow-key navigation, Shift-range selection, current-page Select All, a keyboard-shortcut reference dialog, and permission-aware context menus. Cursor pagination remains unchanged—sorting, filtering, and Select All operate on the current page.
Identity management and high-latency diagnostics
User detail pages display account status, direct policies, group-inherited policies, and Access Key counts; policy pages distinguish native built-in, overridden built-in, and custom policies while showing assignment counts and affected identities. Native built-in policies remain read-only until explicitly overridden, and modified built-in policies offer a one-click Reset. Failed reloads clear stale editor contents, language switches preserve unsaved drafts, and forms bind labels properly while masking remote secrets.
Note: Built-in policy Reset and live bucket inventory require Server 20261026. Upgrading standalone Console by itself does not update an older Server’s policy cache or replace its embedded frontend.
Across all six live diagnostic streams (Health, Trace, Logs, Watch, Speedtest, and Profile), Console now completes the WebSocket close handshake through the single existing reader goroutine with a shared five-second close budget that never cuts off active streams. This fixes the high-RTT health-report truncation and abnormal 1006 closures reported by Azat / @azataiot in #63 (wrapped timeouts still return close code 1008 without leaking internal details). Health-report downloads also reuse a single JSON decoder across the version probe and payload so buffered bytes are never dropped, reject truncated or canceled streams, and retain signed madmin v3 transport and retry behavior.
Fixes in 2.5.1
- Group status toggles (#67): Enabling or disabling a group now preserves its existing members, while an explicit
membersarray continues to replace membership. - OIDC groups and reverse-proxy cookies (#66): Preserves permissions for OIDC string-array group claims and sets
Securesession cookies only when HTTPS or a trusted TLS proxy header is configured. - Credential exports and streaming ZIP errors (#68): Preserves exact UTF-8 JSON bytes when exporting Access Keys, and propagates the S3 HTTP error status when a streaming ZIP download fails before writing its first response byte.
- Accessibility and focus restoration (#68): Restores keyboard focus across tables and dialogs, adds accessible names and progress-bar value attributes, and keeps the focused tab visible when resizing the viewport.
- Companion dependencies (#69): Updates to
mcli 20261026,silo-pkg v3.15.2, and the upstream SDK fix accepting HTTP204when removing bucket replication.
Dependencies and delivery
Console 2.5.1 builds with Go 1.27.3, Node 24.22.1, Vite 8.3.5, GoReleaser 2.19.0, silo-pkg 3.15.2, mcli 20261026, and upstream minio-go 320af050c445, while keeping tablewriter v0.0.5, go-humanize v1.0.1, go-runewidth v0.0.29, and go-systemd v22.6.0 pinned for output and platform compatibility (see the shared dependency and security inventory).
The GitHub release provides platform binaries and archives, Linux packages, source tarballs, SHA-256 checksums, SPDX SBOMs, and Sigstore/GitHub provenance attestations. Windows archives now use .zip (Linux and macOS retain .tar.gz), and multi-architecture container images are published at docker.io/pgsty/silo-console:v2.5.1 (see the release contract for verification commands).
Frontend assets and embedded bundles build reproducibly in isolated workspaces. The supported administration target is pgsty/silo (with best-effort upstream MinIO compatibility), and all existing CONSOLE_* environment variables, API routes, service names, and Go module paths remain unchanged; see the Console compatibility guide for API and embedding details.
18 - silo-pkg 3.15: Go 1.27.3 and S3 SDK Fixes
silo-pkg v3.15.2 is now released (commit 0416eb1f4505) and is adopted directly across the SILO 20261026 stack as github.com/pgsty/silo-pkg/v3. This note summarizes the full 3.15 series (v3.15.0, v3.15.1, and v3.15.2) since v3.14.3.
3.15.0: Toolchain and shared dependencies
The module’s minimum Go version increases from 1.26 to 1.27.3, alongside updates to 13 direct dependencies: JWX 3.4.0, etcd api / client/pkg / client 3.7.3, gRPC 1.84.0, grpc-gateway 2.32.0, go-json 0.11.2, swag/conv 0.29.2, go-colorable 0.1.16, cpuid 2.4.1, fwd 1.2.1, and the two Google generated API/RPC modules pinned at ce908d29299b.
Downstream applications must rebuild with Go 1.27.3 to pick up the Go standard library’s certificate-verification fix. All public package APIs, IAM policy formats, authorization rules, and the v3.14.3 policy evaluation optimizations remain unchanged.
3.15.1: Upstream S3 SDK fixes
v3.15.1 advances upstream minio-go from v7.4.1 to ab93db7ba435, bringing in six fixes:
- Rejects truncated sequential and parallel streamed multipart uploads before transmitting an incomplete part.
- Preserves sequential read state after
ReadAthitsEOFand rejects negativeReadAtoffsets before fetching object metadata. - Fixes read and
Seekbounds after callingObject.Stat. - Deduplicates batch-delete fallback errors so each failed entry is reported once.
3.15.2: Bucket replication removal
v3.15.2 advances upstream minio-go to v7.4.2-0.20261025165926-320af050c445 (a pinned pseudo-version, as upstream has not tagged v7.4.2). Removing bucket replication—including passing an empty configuration to SetBucketReplication—now accepts both HTTP 200 and HTTP 204 while continuing to surface AccessDenied and other real errors (see upstream comparison).
Compatibility and verification
Downstream applications that rely on established CLI formatting and NetBSD builds continue to pin go-humanize v1.0.1, go-runewidth v0.0.29, and go-systemd/v22 v22.6.0. Because Go does not inherit replace directives from dependencies, each consuming application declares its own module replacements.
v3.15.2 passed the full lint and -race test suites (including live LDAP integration), module verification, and all three CI workflows (Go, LDAP, and VulnCheck). HTTP fixtures verified that both replication-removal entry points accept 200/204 while preserving 403 errors, and earlier 3.15 regression suites covered JWT validation boundaries, local etcd auth/watch/lease persistence, and CLI formatting. Scanner finding GO-2026-5932 resides in deprecated OpenPGP packages that silo-pkg does not import (see the shared security inventory).
silo-pkg is a source-only Go module adopted by rebuilding Server, Console, and mcli. The supported dependency graph is the coordinated PGSTY stack, with best-effort upstream MinIO/MC compatibility. GitHub release · Full commit comparison.
19 - Silo Console 2.1.0 Released
Published: 2026-08-06 · Version: v2.1.0 · Repository: pgsty/silo-console
SILO Console 2.1.0 is the first feature release after the independent 2.0.0. It delivers three main changes:
- Bilingual interface — every console screen, help topic, and documentation link now renders in English or Chinese behind a toggle on every page, with zero new runtime dependencies;
- Metrics V3 dashboard — the dashboard moves from MinIO Metrics V2 names onto V3, with explicit handling for the semantics V3 changed underneath it;
- Edge-case correctness fixes — a select-all that matches what a bulk action will delete, placeholders that handle object names containing
$&, timestamps with an explicit timezone, and empty metrics that display “no data” rather than a fabricated0.
This is a minor release. No environment variable, module path, API contract, binary name, or data layout changes. Upgrading is a binary or image swap.
A 2.1.1 patch follows this release
v2.1.1, published the same day, completes the legend hardening described below: a label placeholder the legend builder cannot resolve is now removed instead of leaking literal braces into the Traffic chart legends, the one remaining substitution branch is escape-proofed against label values containing $& or $1, and the License page reports the actual release version instead of 2.0.0. Nothing else changes — upgrade straight to 2.1.1, and everything in this note applies unchanged.
Rebuild your embedded assets if you vendor this console
2.1.0 fixes a packaging defect present on the main branch after 2.0.0: the go:embed payload still carried the 2.0.0 frontend build, so a binary built from an intermediate commit would serve the old UI. The released 2.1.0 artifacts are built from the regenerated payload and are unaffected.
A Bilingual Console
The console is an administration surface for an object store, and a large share of its operators read Chinese first. 2.1.0 makes the interface bilingual without importing an i18n framework — the embedded delivery model means every kilobyte is paid for in the binary. This is issue #6, which proposed i18next; the dependency-free substitution is the one deliberate deviation from it.
How it works
The design constraint was: no new dependency, no build step, no extraction pipeline, and partial coverage must never break the page.
- English source strings are the dictionary keys.
t("Create Bucket")looks up the Chinese entry; a missing key returns the English string unchanged. Coverage can therefore grow incrementally, and a typo degrades to English rather than to a raw key likeconsole.bucket.create. - Three dictionaries, one merge.
zh.ts(165 chrome entries),zhHelp.ts(247 help-topic entries), andzhScreens.ts(1,373 screen entries) merge with chrome taking precedence — about 1,785 entries in total. - The language preference mirrors dark mode:
localStorage→systemSlice→setLanguage. There is no browser-locale detection; the default is English, and the choice is explicit. - Central interception points rather than per-callsite edits: the page-header wrapper, confirm dialogs, help items, route definitions, and the dashboard’s panel renderer each translate on the way out. This is why 220 screen files could be localized without touching their business logic.
- Module split matters.
i18n/lang.tsholds pure primitives (translate,localizeUrl) and imports no store —systemSlicedepends on it, so importing the store back would form a cycle. The hooks (useT,useLanguage,useLocalizedLink) andinterpolate()live ini18n/index.tsx.
The toggle is a stroke-drawn 文/A icon mounted in the page header on every page and reused on the login page.
What it covers
Login and SSO flows, navigation and the command palette, the dashboard and every metrics panel, buckets and the full object browser (uploads, previews, sharing, versioning, rewind), users/groups/policies/access keys, configuration and event destinations, IDP and KMS, logs, health reports, speedtest, profiling, inspect, trace, watch, and the license page.
Beyond visible strings:
- Documentation links localize.
silo.pgsty.comlinks gain a/zhprefix in Chinese; the Pigsty site swaps domains (pigsty.io↔pigsty.cc). GitHub, MinIO, AWS, and YouTube links are left alone. - The help blog feed is per-language, fetching
/zh/blog/index.xmlin Chinese, with an independent cache per language. - The command palette stays searchable in both languages. Menu entries translate for display but keep their English originals as keywords, so “桶” and “buckets” both match.
- Chart legends translate only their static prefix.
translateLegendpreserves instance suffixes like[server:drive], and the data layer keeps raw legends so components that match on them for arithmetic (capacity summing) keep working. - Timestamps are unified, not merely translated — see below.
What it costs
Roughly +61 KB on the embedded payload (2.79 MB → 2.85 MB, +2.2%), zero new dependencies, and the dictionaries land in their own lazily-loaded chunk. The English rendering path is byte-stable: with the default language, output is identical to 2.0.0.
What stays English
Backend error strings (182 of them) are produced by the Go server and are not translatable from the frontend. A handful of strings hardcoded inside the vendored mds component library — the collapsed-menu “Sign Out” tooltip, and the data table’s “Columns”, “Loading…”, and ON/OFF toggles — remain English; two of them (“Sign Out”, “Actions:”) are swapped via a scoped CSS rule, but the rest would require patching the vendor.
Metrics V3 Migration
The dashboard queried MinIO Metrics V2 names. SILO deployments scrape V3 (/minio/metrics/v3), so the dashboard depended on an endpoint the monitoring pipeline no longer collected. 2.1.0 rewrites all 26 widgets onto the V3 catalog — 31 queries over 29 distinct metric names — and drops three widgets (51/61/62) that no layout ever referenced. This is issue #7; the Info-page half is #8.
The decision is V3-only: no runtime fallback, no probing, no version-selection knob. SILO Console targets SILO deployments, where the server, the scrape pipeline, and the console ship together. The SILO server keeps serving V2 endpoints for external consumers; the console simply stopped using them. A fallback would have been actively harmful — a metrics store retaining 15 days of V2 series would let an or-fallback silently read stale data.
The semantics V3 changed
Three properties of V3 break a naive name-for-name rewrite, and each needed a deliberate answer:
- Cluster groups are exported identically by every node.
/cluster/*metrics carry no server label and are not leader-gated, so an N-node scrape yields N duplicate series. Queries aggregate withmax()/min()— neversum(), which would multiply cluster totals by the node count. - Zero values are not exported at all. Any metric whose value is ≤ 0 is skipped. Offline drive counts, healing-drive counts, and erasure-set health simply vanish rather than reporting
0, which a stat card renders as an empty panel. Every affected query carries a companion guard so the panel reads a real0. - There is no
minio_heal_*namespace. The V2 heal activity signal was in-memory anyway — it reset on restart and bumped on any scan. It is replaced by two cards with defensible semantics: Erasure Health (baselined on write quorum) and Usage Data Age (how stale the scanner’s usage snapshot is).
Zero-state semantics
The migration distinguishes zero, no data, and not yet scanned across eight edge cases:
- Capacity free/used baselines on the always-present total, so a full cluster reads
0 freeinstead of vanishing. - Online Drives is guarded against the all-offline case, where the zero-skip would erase the panel when all drives are down.
- Bucket and object counts guard on the usage group’s own freshness gauge, so a cluster that has not completed its first scan reads no data rather than a fabricated
0. - Empty single-value results render as
—, not0. - An empty size distribution no longer fabricates seven zero-height bins.
- Fractional rates stay visible (
parseFloataxis domain, two-decimal CPU formatter) instead of collapsing to0. - Sub-second Usage Data Age clamps to “1 second” instead of rendering blank.
A regression suite (api/admin_info_metrics_test.go) now pins every widget query to the V3 catalog, asserts widget-ID uniqueness, and enforces the per-widget guard taxonomy: health and traffic widgets need a nodes-online companion, usage counts need the usage-group freshness companion, and capacity needs the total baseline. The full mapping is documented in docs/metrics-v3.md.
Also fixed
- Widget 17 queried
sent_bytestwice and widget 11 queriedsyscall_readtwice — both internode/syscall pairs were transposed into duplicates. - Label-less matrices (the result of
max()aggregation) serialize with nometricfield at all, which crashed the frontend’s label extraction and produced a0 Bcapacity donut and an empty usage-growth chart. Guarded. - An unused per-widget Prometheus label-values prefetch stalled every widget request by up to a second. Deleted.
- The dashboard’s usage cards, chart controls, and dense Traffic/Resources panels were rebuilt on one grammar and now reflow through tablet widths.
Two server-side bugs were identified during this work and are tracked upstream rather than worked around here: minio_cluster_usage_buckets_since_last_update_seconds emits nanoseconds (the objects variant is correct), and V3 bucket-level sent/received traffic are transposed.
Correctness Fixes
Placeholders that survive real object names
String.prototype.replace interprets $&, $', $`, and $1 in the replacement value as directives. S3 keys legally contain $. So an object named report$&.csv did not render as itself — it re-injected the matched placeholder text into the output and corrupted the message. All 37 dictionary placeholder substitutions now pass the value through a function replacement, where no such interpretation happens. This bug existed in the original English UI and surfaced during the localization audit.
A select-all that matches bulk actions
The vendored data table renders a plain untranslatable “Select” header whenever onSelectAll is absent — which was the case on all seven selectable tables. Worse, the naive fix is wrong: a select-all that replaces the whole selection drops rows hidden by an active filter, so the header checkbox and a subsequent bulk action can target different sets. The implementation toggles only the currently visible rows and preserves filter-hidden selections, so the header state can no longer imply a different set than the action would touch.
Timestamps with a timezone
Bucket, object, version, rewind, and access-key timestamps rendered as a mix of verbose English forms and — in several places — a 12-hour clock without AM/PM, which is simply ambiguous. All of them now render as yyyy-MM-dd HH:mm[:ss] (ZZZZ) in both languages.
A translation runtime that survives live data
t() also receives runtime strings: user agents, RSS titles, object names. Two hardening changes followed:
- misses return unchanged, unconditionally — the implicit
@contextsuffix stripping is gone, because it silently mutated live data that happened to contain@; - dictionary lookups are guarded with
hasOwnProperty, so a hostile input naming an inheritedObject.prototypemember (constructor,toString) cannot leak a function into the UI.
Interaction and accessibility
- An expired session opening a deep link bounced through
/loginand back, accumulating a redirect chain instead of landing on the form once (#1). - Collapsed sidebar buttons carried no accessible name; screen readers announced them as unlabelled (#4). Access Key inputs now declare their autocomplete intent instead of letting password managers guess (#5).
- Mobile metrics and bucket panels scroll instead of clipping (#3).
- The speedtest control row wraps instead of overflowing its card, its duration accepts seconds or minutes, and its size defaults to MiB to match its own unit list.
- Sidebar bucket rows use a virtual row pitch matching the 44px item, so selected and hovered highlights no longer overlap.
- Unit chips render the selected unit’s label rather than its raw value.
No SUBNET, No Telemetry
Upstream removed Subnet, Registration, and Call Home; this fork inherited that state but still carried three traces. 2.1.0 removes them:
- the health websocket’s
subnetResponsefield never addressed a subnet — it is a sentinel meaning “the report was assembled” — and is nowreportStatus: "ok"; - two help topics claimed the health report “uploads automatically to SUBNET” and that inspect output is “transmitted to SILO SUBNET”. Neither was true. They now describe what happens: the report is generated on the deployment and downloaded by the browser;
- the unreferenced
CONSOLE_SUBNET_PROXYconstant is deleted.
For the record, 2.1.0’s outbound network posture is unchanged and remains: no analytics, no telemetry, no beacons, no external scripts or fonts. silo-console update is still disabled. The release catalog is contacted only if SILO_RELEASE_SERVICE_HOST (or RELEASE_SERVICE_HOST) is explicitly set — there is no default. The only automatic outbound request the browser makes is the help panel’s blog feed, and only after a user opens the Blog tab.
Upgrade Guide
There is nothing to migrate. No environment variable, module path, protocol field, systemd unit, binary name, or data layout changes between 2.0.0 and 2.1.0.
Two things are worth knowing:
- The dashboard now requires Metrics V3. If your Prometheus scrapes only the V2 endpoints, dashboard panels will read no-data. Point the scrape at
/minio/metrics/v3; Pigsty-managed deployments already do. - The language default is English, chosen per browser and stored in
localStorage. There is no server-side default and no browser-locale detection, so no existing deployment changes appearance on upgrade.
Verification Scope
Before tagging, the following verification checks ran against the final tree: go build, go vet, golangci-lint (0 issues), the Go unit suite across all packages, gofmt, TypeScript type checking, the frontend production build, Prettier across all sources, dictionary duplicate-key checks, and a debug-leftover scan of the complete diff.
The embedded payload was rebuilt twice from a clean directory and confirmed byte-identical for the release pipeline’s zero-diff gate. Metrics V3 queries were validated against a live metrics store with real cluster data.
Known Limitations
- The SSO end-to-end suite requires an external OpenLDAP/Dex/MinIO topology and was not run in that environment this cycle; the OIDC code paths are covered by unit tests.
- Backend error strings and several vendored
mdscomponent strings remain English (see What stays English). - Chinese translation covers the console’s own surfaces; help-topic bodies are translated, but the documentation pages they link to follow the docs site’s own language coverage.
- Two server-side V3 metric bugs (nanosecond bucket-usage age, transposed bucket traffic) are tracked upstream and are not worked around in the console.
- Automatic self-update remains disabled; upgrades are explicit.
Issues Closed
2.1.0 closes every issue filed against 2.0.0. Each carries a comment on the tracker describing the fix, the commits, and the coverage added.
| Issue | Resolution |
|---|---|
#1 — unauthenticated deep routes recurse /login |
Absolute, base-path-aware login destination; deep-link and subpath test coverage |
| #2 — stale Uptime, malformed legends, cramped menus | Uptime derived from real server state, legends resolve on the V3 name label, 32 px chart controls, popup width floors |
| #3 — 390 px viewport clips content | Scrollable metrics tab strip; bucket table with a deliberate mobile column budget |
| #4 — unnamed collapsed sidebar buttons | Labels visually hidden rather than removed from the accessibility tree; named, keyboard-operable collapse toggle |
| #5 — Access Key fields lack autocomplete metadata | Field-level username / new-password tokens in a dedicated autofill section |
| #6 — English/Chinese localization | Hand-rolled bilingual layer, zero new dependencies, English-as-key fallback |
| #7 — migrate monitoring queries to Metrics V3 | V3-only; 26 widgets, 31 queries, 29 metric names, guard taxonomy, regression suite |
| #8 — replace N/A Info metrics | Erasure Health and Usage Data Age, sharing the advanced dashboard’s widget results |
Three acceptance criteria are recorded as unmet rather than quietly ticked: web-app has no unit-test runner, so the i18n test suite (#6) and the focused constructLabelNames test (#2) would require introducing test tooling first, and #6’s contributor documentation for adding translation keys is not yet written.
Related Commits and Links
The complete v2.1.0 change set consists of 20 logical commits. The v2.1.0 tag additionally carries three later documentation commits that rewrote the repository README; they change no shipped behavior.
8764f5d— fix(web): stop recursive login redirects437c56c— fix(ui): make the dashboard and bucket list usable on narrow screens85fc0c6— fix(a11y): name collapsed sidebar controls and credential fieldse3fed07— fix(metrics): rebuild dashboard cards, chart controls, and layoutfa11576— feat(login): polish controls and legal attribution9fc17c1— feat(i18n): add hand-rolled EN/ZH core, dictionaries, and language toggle622c02e— feat(i18n): localize login, navigation, and the help system6a03719— feat(i18n): localize dashboard and metrics screens14b1c2d— feat(i18n): localize bucket and object browser screens0298062— feat(i18n): localize identity, configuration, and event destinations41094f6— feat(i18n): localize observability, admin tools, and shared componentse964992— feat(metrics): migrate the dashboard to MinIO Metrics V30b2251f— fix(i18n): harden the translation runtime for live data and chart legends9b60148— fix(console): unify timestamps on a timezone-carrying standard formatbf110ae— fix(console): give selectable tables a visible-rows select-all5fc8f22— fix(i18n): escape-proof all placeholder substitutionsfef8fab— fix(console): polish speedtest, sidebar, and help chromec4911e8— chore(console): drop SUBNET remnants from health reporting1d631c4— docs: record the SILO Console v2.1.0 changelog912d847— build: regenerate optimized embedded web assets
Links:
20 - Silo Console 2.0.0 Released
Published: 2026-08-04 · Version: v2.0.0 · Repository: pgsty/silo-console
SILO Console 2.0.0 is the first major release of this object-storage administration console as an independent project. Continuing from the georgmangold/console v1.9.1 maintenance line, it accomplishes three things:
- An independent identity — product name, visual system, documentation entry points, source attribution, and the release pipeline all move into the SILO project, while the Go module path, environment variables, and other compatibility contracts are deliberately retained;
- A redesigned interface — the login page, theme system, dashboard, and console details are reworked under one design language, backed by a regenerated brand icon set;
- Hardened engineering — the embedded frontend payload shrinks from roughly 10MB to 3.5MB, known dependency vulnerabilities drop to zero, and a batch of inherited defects — including a runtime data race — is fixed.
Read the compatibility boundary before upgrading
Version 2.0.0 updates public identity and delivery contracts while keeping the object data format and S3 protocol unchanged. Installation scripts that reference the old repository, binary name, or container image must be updated; existing integrations that use CONSOLE_MINIO_SERVER, CONSOLE_MINIO_REGION, github.com/minio/console, or the MinIO-compatible Admin API remain unchanged and must not be search-and-replaced.
Why 2.0.0
This console originated as MinIO Console and was carried forward by the Alevsk/console and georgmangold/console community maintenance lines. SILO Console continues from there, maintained by the Pigsty community as the browser-based administration interface for SILO.
The version jumps from v1.9.1 to v2.0.0 because these public contracts change together:
- the product is now uniformly SILO Console, with the primary repository at
pgsty/silo-console; - the release binary changes from
consoletosilo-console; - release assets, checksums, package metadata, CLI descriptions, and project links all switch to SILO;
- in-product identity, help entry points, copyright attribution, source offers, and trademark notices are re-established.
The migration strategy is “clear external identity, restrained internal compatibility”: operators must take notice, but the underlying compatibility interfaces are not mechanically renamed.
Naming and Delivery Contracts
| Scope | Previous name or location | 2.0.0 contract |
|---|---|---|
| Product | Console / legacy MinIO Console | SILO Console |
| Repository | georgmangold/console |
pgsty/silo-console |
| Release binary | console |
silo-console |
| Binary assets | console-<os>-<arch> |
silo-console-<os>-<arch> |
| Checksums | console_<version>_checksums.txt |
silo-console_<version>_checksums.txt |
| Website and docs | upstream / previous maintainer | silo.pgsty.com and silo.pgsty.com/docs/ |
For container deployments, use pgsty/silo-console on Docker Hub. See the Console v2.4.1 release notes for the current version and pull command.
CLI authorship, usage text, and project descriptions now identify Pigsty and SILO Console. DEB/RPM/APK vendor, maintainer, homepage, description, and license metadata are updated accordingly; the executable installs to /usr/local/bin/silo-console.
Deliberately Retained Compatibility Identifiers
The following names retain minio or console as part of interface, protocol, or installation compatibility:
| Surface | State in 2.0.0 | Reason |
|---|---|---|
| Go module | github.com/minio/console retained |
changing it breaks every Go import |
| Server endpoint | CONSOLE_MINIO_SERVER retained |
widely used by existing deployments |
| Server region | CONSOLE_MINIO_REGION retained |
existing compatibility contract |
| Other configuration | existing CONSOLE_* variables remain valid |
avoids migration with no benefit |
| S3/Admin API names | MinIO-compatible fields and enums retained | they describe the actual protocol |
| Development build | make console still produces ./console |
keeps developer workflows working |
| Package systemd unit | minio-console.service retained |
avoids duplicate services on upgrade |
| systemd user and config | console-user and /etc/default/console |
avoids unnecessary account/config migration |
Upgrade scripts therefore must not run repository-wide minio → silo or console → silo-console replacements. Migrating these compatibility interfaces in the future will require aliases, deprecation windows, and an explicit dual-read strategy.
A Redesigned Interface
Version 2.0.0 redesigns the console interface end to end.
Login page
The login page is rewritten from scratch. The left brand panel renders a slowly drifting sine-mesh animation generated purely on Canvas (zero external dependencies, honors prefers-reduced-motion, pauses in background tabs), states the project’s proposition — “Keep the S3 Interface / Own the Object Store” — and keeps the full MinIO trademark notice at the bottom. The right-hand form is functionally untouched, preserving every existing automation selector. The Chakra Petch typeface used by the SILO wordmark ships as a ~20KB locally bundled subset with no external requests.
A unified theme system
All console colors converge into one light/dark theme layer: neutral greys for text and borders, the brand steel blue for primary actions and selection, and a sidebar that uses the same night palette as the login panel in both modes. Controls and cards share consistent radii and transitions, inputs get a keyboard focus ring, and modals animate in (also honoring reduced motion). Server-provided customStyles keep full precedence.
Console polish
- Dashboard (Metrics): stat cards rebuilt under one grammar — muted labels, tabular numerals, aligned status dots; charts and info strips are theme-driven; the upstream absolute-positioning layout is gone.
- Unified empty states: placeholder text in Watch, Trace, bucket Events/Replication/Lifecycle, and every other data panel is now centered and de-emphasized instead of raw top-left text.
- Vertical tabs: detail-page tabs change from bordered grey blocks to a quiet pill list, eliminating the stray empty cell at the bottom of the rail.
- License page: a new VERSION section shows both the connected server’s release and the Console’s own version; accounts without
admin:ServerInfonever issue the request and the row stays hidden. The page also consolidates AGPLv3 licensing, the AGPL section-13 source offer, lineage, and trademark boundaries. - A batch of interaction fixes: the sidebar now collapses on initial load at mobile widths (previously it waited for a resize event); the bottom navigation no longer lags window-height changes; the bucket accordion highlight spans the full row; the dashboard no longer overflows horizontally on narrow screens; and the help panel is now truly lazy — the login page makes no external requests at all.
Brand icon set
The favicon, PWA, and Apple Touch icons still carried a previous-generation hand-drawn emblem. 2.0.0 re-rasterizes every size (ico 16+32, favicon 16/32/96, apple 180, manifest 192/512) from the official silo.svg vector emblem, with safe-area margins on home-screen sizes, and trims the Web App Manifest to the modern icon set, dropping the 2014-era legacy density entries. The icon payload drops from 473KB to 160KB, and the browser tab icon finally matches the in-product brand.
Smaller and Faster
Embedded delivery is this console’s core form factor — the frontend ships inside the binary via go:embed. 2.0.0 optimizes that path systematically:
- Embedded payload: ~9.6MB → 3.5MB. Text assets (JS/CSS/SVG/…) are precompressed at build time with deterministic gzip and embedded compressed-only; legacy WOFF fonts (~1.25MB that no supported browser ever downloads) and a set of entirely unreferenced orphan images are removed.
- First-load transfer: ~5.7MB → ~1.7MB. Static assets previously shipped uncompressed on the wire; they are now emitted directly with
Content-Encoding: gzipat zero runtime cost, with on-the-fly decompression for the rare client that does not accept gzip. - Correct HTTP semantics. Accept-Encoding is parsed with full RFC 9110 q-values (
gzip;q=0gets identity bytes), responses carryVary: Accept-Encoding, and non-GET/HEAD requests to static paths and the SPA entry receive 405 with anAllowheader. - Reproducible builds. Compression uses a pure-JS implementation (fflate) for byte-identical output across platforms, and the release pipeline enforces a hard gate: rebuilding the embedded assets in a clean environment must produce zero diff against the commit.
Release binaries (all frontend assets included, stripped) weigh roughly 35–40MB; for the downstream SILO server, embedding this console now costs about 3.5MB instead of about 10MB.
Security and Dependencies
Go: the build baseline moves to Go 1.26.5 and the golang.org/x family is fully refreshed. Every reachable vulnerability reported by govulncheck is resolved:
| Dependency | Fixed version | Advisories |
|---|---|---|
google.golang.org/grpc |
v1.82.1 | GO-2026-6061 |
github.com/prometheus/prometheus |
v0.311.3 | GO-2026-5710 / -5662 / -5381 / -5264 (incl. remote-read DoS) |
github.com/klauspost/compress |
v1.18.7 | GO-2026-5841 |
The single remaining advisory sits in golang.org/x/crypto, has no upstream fix yet, and is unreachable from this codebase; it is tracked as a known item.
Frontend: the full dependency-tree audit (production and tooling) is clean, covering the high-severity form-data CRLF injection and the DOMPurify and qs advisories; React Router is migrated to 7.18.2 (keeping the v6-compatible declarative API, with full routing regression). The only explicitly ignored advisory affects an unstable API this project does not use.
Runtime correctness: a real data race between HTTP log-target initialization and shutdown is fixed, along with shared-mock races in the test suite; supported Go packages pass -race across the board. As a side benefit, the go-m1cpu upgrade fixes the local go run cgo crash on recent macOS.
Update Checks and Default Network Behavior
This release keeps conservative defaults for upgrade tooling:
- automatic self-update in
silo-console updateis disabled — the command prints guidance and never downloads or replaces the binary; - the release catalog gains
SILO_RELEASE_SERVICE_HOST, with the previousRELEASE_SERVICE_HOSTas a compatibility fallback; with neither set, no remote release service is contacted; - the help panel’s blog content loads only when opened, and its links accept
https://silo.pgsty.comexclusively.
Automatic updates will be reconsidered once signed release assets and a tested rollback path are in place.
Release Artifacts and Platform Matrix
The release ships 16 assets:
| Type | Coverage |
|---|---|
| Standalone binary | Linux amd64/arm64/arm, macOS amd64/arm64, Windows amd64 |
| System packages | DEB / RPM / APK × amd64/arm64/armv6 |
| Checksums | silo-console_2.0.0_checksums.txt (SHA-256) |
The pipeline triggers on tag pushes, pins third-party Actions to commit SHAs, and enforces the clean-checkout and zero-diff asset-rebuild gates before GoReleaser runs.
Upgrade Guide
Standalone binary
When building from source, make console still produces ./console; install it under the release name before wiring it into a production service.
DEB/RPM/APK and systemd
Packages continue to install /etc/systemd/system/minio-console.service, whose unit starts /usr/local/bin/silo-console. EnvironmentFile=/etc/default/console, console-user, and existing CONSOLE_* variables are unchanged. This retention lets package upgrades keep acting on the existing service instead of creating a parallel one.
Configuration and integrations
- do not rename
CONSOLE_MINIO_SERVERorCONSOLE_MINIO_REGION; - do not touch
github.com/minio/consolein Go imports; - prefer
SILO_RELEASE_SERVICE_HOSTfor self-hosted release catalogs; - replace any reliance on
console updatewith explicit download, verification, and deployment; - update process-path-based monitoring to
/usr/local/bin/silo-console.
This release does not change the object data layout and requires no bucket or object migration.
Validation Scope
Release verification covered Go -race across supported packages, go vet, golangci-lint, govulncheck, frontend type checks, production builds, Prettier, dead-code checks, full dependency audits, and the following integration checks:
- all 184 embedded files fetched three ways each (gzip client, identity client, HEAD) with per-file hash comparison against the embedded sources;
- RFC semantics probes (including combined q-values such as
gzip;q=0, *;q=0.5), method restrictions, the OIDC callback, and SPA deep links; - full React Router 7 regression: deep links, client-side navigation, bucket-detail tab switching, and browser history back;
- mobile first-load sidebar behavior, login-page external-request monitoring, and light/dark full-site tours;
- downloaded release assets verified byte-for-byte against checksums, binary self-reported version confirmed, and a smoke test of the published binary against a live server;
- zero-diff asset rebuilds confirmed on both macOS and Linux.
Known Limitations
- automatic self-update is disabled; upgrades are explicit;
- the SSO end-to-end suite requires an external OpenLDAP/Dex/MinIO topology and was not run in that environment this cycle (the OIDC code paths are covered by unit tests and HTTP-level checks);
- one
golang.org/x/cryptoadvisory has no upstream fix yet and is unreachable from this codebase; - SILO does not yet maintain its own video library; videos in the help panel are clearly labeled upstream compatibility material;
- administrative features depend on the MinIO-compatible Admin API — SILO Console is not a generic browser for arbitrary S3 services;
- retained Go module paths, environment variables, protocol fields, and the systemd unit name still appear in code, configuration, and process listings.
Related Commits and Links
The complete v2.0.0 change set consists of 8 logical commits:
50797de— feat: establish SILO Console identity and compatibility23ae6e8— feat: redesign and harden the SILO Console web app7a83a77— build: update Go toolchain and dependencies1330d25— fix: eliminate logger shutdown and test mock races06b3a34— docs: publish the SILO Console v2.0.0 guide4b24372— build: regenerate optimized embedded web assetsc38eb64— ci: package and publish SILO Console v2 releasesb952a12— brand: regenerate the icon set from the official silo.svg emblem
Links:
21 - silo-pkg 3.13.0 Released
Release date: 2026-08-30 · Version: v3.13.0 · Commit: 215f116 · Repository: pgsty/silo-pkg
Version 3.13.0 is a breaking release: the module adopts github.com/pgsty/silo-pkg/v3 as its own import path and retires the pgsty/silo-go fork in favour of upstream minio-go. All maintained SILO components (silo, silo-console, and mcli) migrated to this module path in the 2026-09-03 / v2.4.0 release wave; see the component matrix.
Release at a Glance
- Published: the
silo-pkg v3.13.0tag and GitHub Release, declaringmodule github.com/pgsty/silo-pkg/v3. - Retired: the
github.com/minio/pkg/v3module identity and thereplacedirective every consumer had to repeat. - Retired: the
github.com/pgsty/silo-go/v7fork; this release requires upstreamminio-godirectly. - Unchanged: every package, symbol, and behaviour. Only the path they are imported from moved.
- Downstream adoption: completed across SILO 20260903, Console v2.4.0, and mcli 20260903.
Why the Path Moved
The fork kept upstream’s github.com/minio/pkg/v3 path so it stayed a drop-in replacement, selectable with one replace directive. Go does not inherit replace directives from dependency modules, and three costs came due while preparing the next SILO server release.
Every consumer had to repeat the redirect, and one that forgot built against upstream silently. A module that requires this package but omits the replacement resolves the real minio/pkg, compiles, and quietly loses the fork’s behaviour.
The require line had to name a version the source no longer matched. ParseConfigStrict first appears upstream in v3.11.0, and Resource.IsBareARN exists in no upstream version at all. A require github.com/minio/pkg/v3 v3.6.1 next to source that needs v3.12 APIs is metadata that cannot be made true by editing the number.
Working around that propagated. pgsty/mc carried a compile-time sentinel — a reference to Resource.IsBareARN — purely to turn the silent downgrade into a build failure. Raising mc’s floor to make its metadata honest pushed the requirement through the module graph and forced silo-console’s deliberately low floor upward, which is exactly what that floor existed to prevent.
Upstream’s path had little to offer in return here. minio/pkg is a small internal library, and this module’s consumers are silo, silo-console, and mcli. The Silo Go SDK keeps upstream’s github.com/minio/minio-go/v7 path, where drop-in compatibility is worth having and upstream is actively maintained.
Upstream minio-go Replaces the Silo Go SDK
pgsty/silo-go no longer carried any functional divergence. Its one unique change, Return CopyObject checksums in UploadInfo, was merged upstream on 2026-08-24. Everything else in the fork was a version string, a logo, a README, and a lint-tool block.
Upstream’s newest tag v7.3.0 predates that merge by 14 commits, among them a data race fix in parallel multipart checksum hashing, so pinning the tag would be a regression. This release therefore requires the pseudo-version v7.3.1-0.20260828014306-0e78d3f18efe and will move to a tag when upstream cuts one.
Compatibility
Every package, exported symbol, and behaviour is unchanged. This release moves where they are imported from, nothing else.
Take it together with the matching silo, silo-console, and mcli changes. A consumer that upgrades alone will not build, because the old and new paths are different modules and types do not cross between them.
Before:
After:
…and rewrite github.com/minio/pkg/v3/... imports to github.com/pgsty/silo-pkg/v3/.... Consumers that stay on the old arrangement keep building against v3.12.2 and earlier, which remain published.
One consequence is worth expecting. Because the two paths are now separate modules, a build can contain both — a third-party dependency that imports github.com/minio/pkg/v3 no longer has its import redirected. In the SILO stack this happens once, through minio/colorjson and minio/dperf, both of which reach only pkg/v3/console. That package’s colour switch is fatih/color’s process-wide NoColor, which every copy shares, so disabling colour still disables it everywhere.
Verification
go build ./...,go vet ./...,go mod tidy -diff, andgofmt -l .are clean;go test ./...passes 23 packages.- CI on the release commit: Go, LDAP Config Validator, VulnCheck.
- The new path was resolved from the module proxy by a fresh module with no
replacedirective, which importedpolicyand ran. - The downstream migration was validated end to end before this release:
mcli(194 files),silo-console(36 files), andsilo(181 files) all build, vet, and pass their suites.silo’s rebrand guard passes with a compatibility-baseline diff of exactly 19 deleted import entries and no change to environment variables, metrics, headers, routes, policy values, or exported symbols.
22 - silo-pkg 3.12.0 Released
Release date: 2026-08-24 · Version: v3.12.0 · Commit: 2b087a1 · Repository: pgsty/silo-pkg
Version 3.12.0 is a main-line minor release with two themes: a policy-write guard for ARN prefixes that name no resource, and the maintained Go 1.27 / etcd 3.7 dependency baseline. It adds two exported inspection/validation methods, raises the verified consumer floor to Go 1.26, and is the first silo-pkg release whose strict validation path is enabled by the SILO server (eee05a17c, SN-2026-005, shipped in SILO 20260903) for named-policy and service-account policy writes.
Release at a Glance
- Published: the
silo-pkg v3.12.0tag, GitHub Release, and strict library validation for bare ARN prefixes. - Server integration: named-policy and service-account policy write validation in
eee05a17c(SILO 20260903). - Deliberately permissive: existing IAM policy loading, IAM import, and site-replication receive paths.
- Deferred: STS inline-policy strict validation and Console-side early validation; the server remains authoritative.
Bare ARN Prefixes Were Policy No-ops
An S3 resource ARN needs a resource after its namespace prefix:
The policy parser also accepted the prefix by itself:
That string names no bucket or object. The existing parser normalized it to the wildcard resource type while retaining arn:aws:s3::: as the match pattern. Real S3 authorization candidates look like bucket or bucket/object, so the pattern normally matched nothing even though the policy validated successfully.
For statements that actually perform resource matching, the impact depends on Effect and on whether the prefix appears in Resource or NotResource:
| Statement shape | Existing runtime result |
|---|---|
Allow + bare Resource |
Grants nothing |
Deny + bare Resource |
The intended denial does not fire |
Allow + bare NotResource |
Excludes nothing and can grant far more than intended |
Deny + bare NotResource |
Can deny far more than intended |
The dangerous cases are policy-dependent fail-open behavior, not an unauthenticated remote exploit and not a CVE. A policy author, template, or automation must first submit the malformed resource. The same issue applies to registered S3 Tables and KMS ARN prefixes.
Resource-less admin actions, sts:* action statements, and the first phase of two-step KMS authorization bypass resource matching; a bare prefix does not change their existing runtime decision. Strict writes still reject the deceptive field so a policy cannot look scoped when that scope is ignored.
The Historical *arn:... Spelling
On the permissive compatibility path inherited from earlier releases, serializing a parsed bare prefix adds the wildcard type marker:
Re-parsing either spelling produces the same internal resource value. The 3.12 guard therefore recognizes both the exact prefix and its historical star-prefixed serialization. This matters for stored/exported policies and for clients that parse and marshal a document before sending it to the server.
The accepted wildcard corpus remains unchanged: *, **, ***, *foo, and explicit resources such as arn:aws:s3:::* still parse as before.
A Strict Write Path, Not a Storage Migration
The fix deliberately separates policy loading from policy creation:
ParseConfigandValidateremain permissive. Existing stored policies keep loading and evaluating with the same matching and serialization behavior.ParseConfigStrictandValidateStrictreject a registered ARN prefix that names no resource, in bothResourceandNotResource.Resource.IsBareARN()detects the normalized exact/historical form without changing the exportedResourcestructure,ParseResource, matching, or JSON representation.ResourceSet.ValidateStrict()exposes the strict resource-set check to consumers.
Keeping the existing resource representation is important for mixed-version sites: v3.11 and v3.12 nodes continue to compare and serialize stored policies the same way, so this fix does not create a site-replication mismatch or require a storage migration.
SILO Enables the Guard on Three Boundaries
SILO commit eee05a17c selects silo-pkg v3.12.0 and uses strict parsing when:
- creating or replacing a named IAM policy;
- creating a service account with an inline session policy; and
- updating a service account’s inline session policy.
Compatibility-sensitive paths stay permissive in this rollout:
- loading named policies and embedded policies already at rest;
- IAM import/restore;
- site-replication receive and apply paths;
- STS inline session policies; and
- bucket policies, whose existing bucket/action validation already rejects these forms.
Enabling ParseConfigStrict also activates two pre-existing admin-policy checks: one admin statement may not contain both Resource and NotResource, and a bucket-scoped admin action may not use a non-S3 resource. These are intentional authorization tightenings and are documented in the advisory ledger under SN-2026-005.
In this article, bare ARN prefix means an ARN namespace with no resource after it, such as arn:aws:s3:::. It is different from the valid bare bucket ARN used in the 3.11 bucket/object-boundary fix, such as arn:aws:s3:::my-bucket.
Operator Action
Existing policies are not rewritten automatically because the intended resource cannot be inferred. Before deploying a SILO server build that contains the strict integration:
- inspect named IAM policies for exact or historical bare prefixes;
- inspect service-account inline policies;
- replace each finding with the intended concrete resource; or use a suffix wildcard only if all resources in that namespace are truly intended; and
- repeat the audit after all sites have completed the rolling upgrade.
Do not blindly turn every finding into arn:aws:s3:::*: that could replace an inert statement with a cluster-wide grant or denial. A legacy policy containing a bare prefix still loads, but it cannot be submitted unchanged through the three strict write endpoints; correct it before editing another property on the same policy or service account.
The safer audit path uses policy-info and access-key-info APIs rather than a full IAM export, because a complete export contains user and service-account secrets. STS strict validation remains deferred until live machine clients and their session-policy templates can be audited separately.
Why This Is a Minor Release
This is v3.12.0, not v3.11.1, because the release combines three compatibility-relevant changes:
- the etcd client crosses from the 3.6 minor line to 3.7;
- the module’s verified
gofloor rises from 1.25.0 to 1.26.0; and - the package adds exported bare-ARN inspection and strict resource-set validation APIs.
The module path remains github.com/minio/pkg/v3; the /v3 import suffix and every existing import site stay unchanged.
Go and Dependency Baseline
The go and toolchain directives have separate purposes:
go 1.26.0is the supported consumer floor required by the selected etcd 3.7 modules.toolchain go1.27.0is the maintained development and CI baseline.- CI actions move to the Node 24 runtime.
Key selected versions change as follows:
| Module | 3.11.0 | 3.12.0 |
|---|---|---|
go.etcd.io/etcd/{api,client/pkg,client}/v3 |
3.6.6 |
3.7.1 |
golang.org/x/crypto |
0.54.0 |
0.55.0 |
golang.org/x/net |
0.57.0 |
0.58.0 |
golang.org/x/text |
0.40.0 |
0.41.0 |
github.com/minio/minio-go/v7 |
7.0.97 |
7.0.99 |
github.com/minio/mux |
1.8.2 |
1.9.2 |
github.com/cheggaaa/pb |
1.0.29 |
1.0.30 |
github.com/lestrrat-go/jwx/v3 |
3.0.12 |
3.0.13 |
github.com/lestrrat-go/httprc/v3 |
3.0.1 |
3.0.6 |
github.com/grpc-ecosystem/grpc-gateway/v2 |
2.27.3 |
2.29.0 |
go.uber.org/zap |
1.27.1 |
1.28.0 |
Smaller selected updates include uax29 2.3.1, fastjson 1.6.10, secp256k1 4.4.1, and goccy/go-json 0.10.6. The old lestrrat-go/option v1, gogo/protobuf, and stale test-only requirements leave the selected graph.
etcd 3.7.1 is the first fixed release on the 3.7 line for GO-2026-6107 / CVE-2026-73500, an unauthenticated TLS-listener denial of service. Updating these Go client modules does not upgrade an operator’s external etcd server. SILO does not use the removed grpc.WithBlock behavior, and client compatibility with an etcd 3.6.14 server was exercised during release validation.
The dependency graph declares coreos/go-systemd 22.7.0, but the module replaces it with 22.6.0 because 22.7.0 does not compile on NetBSD. The SILO server carries the same portability override.
Compatibility
- Import paths and the module major remain unchanged.
- Existing policies keep loading and evaluating unchanged; only strict create/update calls reject the malformed prefixes.
- No policy, IAM database, wire protocol, or etcd data migration is performed.
- Downstream users of
grpc.WithBlockin etcd client dial options must migrate to a supported readiness check; SILO does not use it. - A real external etcd cluster upgrade remains a separate operational procedure.
- Upstream AIStor Memory and new AIStor-only S3 Tables action vocabularies are not imported by this fork.
Consumers select the release with:
Verification
The tagged package passed:
- the repository’s complete
make testgate: pinned lint plusgo test -race -tags kqueue ./...; - targeted bare-ARN tests repeated to disturb Go map iteration order; and
go mod verify,go vet,git diff --check, andgovulncheckwith zero reachable vulnerabilities.
The SILO integration passed:
- the complete IAM server suite, including exact/historical named-policy rejection and service-account create/update rejection;
go test ./cmd -count=1,go vet ./cmd, andgo test ./...;- golangci-lint 2.13.1 with zero findings,
go mod verify, andmake check-gen; and - mutation tests proving all three strict call sites and their integration assertions are load-bearing.
Direct VCS module resolution verified v3.12.0 at commit 2b087a1 with module checksum:
The Git tag, GitHub Release, direct module archive, origin commit, and checksum were verified.
Related Changes
2bc3a91: move CI actions onto Node 24c8c6872: align the SILO Go dependency stack2b087a1: reject bare ARN prefixes on strict policy writes; taggedv3.12.030c49bd: update the README dependency example after the tageee05a17c: enable strict named-policy and service-account writes in SILO56c67dacf: recordSN-2026-005
Server Release Scope
The server-side strict policy-write integration (eee05a17c) shipped in SILO 20260903. STS inline-policy strict validation remains deferred so machine clients and session-policy templates can be audited separately.
23 - Silo Pkg 3.11.0 Released
Release date: 2026-08-04 · Version: v3.11.0 · Commit: d8b1fa7 · Repository: pgsty/silo-pkg
This is the fork’s first pinned release. It restores the IAM bucket/object resource boundary reported as upstream minio/minio#20449: a policy condition-key bypass fix, three LDAP connection defects, a certificate watcher leak, a seeded-RNG defect, and the module’s real minimum Go version.
Two things to check before upgrading
- This release tightens authorization. Twelve bucket-level write actions are no longer reachable through an object-only resource pattern such as
arn:aws:s3:::bucket/*. If you write your own bucket-scoped policies, read The IAM bucket/object boundary — the fix is one line of policy for anyone affected, andMINIO_API_LEGACY_BUCKET_RESOURCE_MATCH=onrestores the previous behaviour in full. - Pair with the server condition-key update. The policy lookup change and the server changes that reserve internal condition-key names each cover one half of the condition-key fix. The companion server work (
2f55347f7) ships in SILO 20260804.
What This Repository Is
silo-pkg is a maintained fork of minio/pkg, carrying fixes needed by community MinIO forks that the now commercially driven upstream no longer accepts. The repository was renamed from pgsty/minio-pkg on 2026-08-02.
The module path intentionally remains unchanged as github.com/minio/pkg/v3. Existing import "github.com/minio/pkg/v3/..." statements do not change; only the right-hand side of the replace directive does:
The /v3 suffix is the module’s major version, not a directory name, and must not be omitted. It is also why this release is numbered v3.11.0 rather than v4.0.0: Go requires the major version of a tag to match the major-version suffix declared in go.mod, so a v4.0.0 tag on a .../v3 module is rejected by the toolchain. Publishing a real v4 would mean changing the module path and rewriting roughly 395 import sites across the server, mc and Console — abandoning the drop-in property that is the point of keeping upstream’s path.
The IAM Bucket/Object Boundary
Every bucket-level S3 operation authorizes with an empty object name. The IAM matcher turned that into a resource string and, for the empty-object case, appended a trailing slash:
"bucket/" is matched by the wildcard pattern "bucket/*", because * matches the empty string. A policy granting s3:* on arn:aws:s3:::bucket/* — which reads as “anything, but only on the objects in this bucket” — therefore also authorized bucket-level actions. In a multi-tenant cluster, a tenant holding only that grant could call PutBucketPolicy and install {"Principal":"*"}, making the bucket publicly readable or writable, or grant itself bucket-level control. It could also delete the bucket outright, which is the reproduction in the upstream issue.
The bucket-policy evaluation path used for anonymous access never had this slash and was already reference-correct. Only the IAM path was wrong, in exactly one place.
Why not correct the whole boundary
Removing the slash for every bucket-level request is the obvious fix, and upstream tried it: the change was reverted the same day for breaking policies that relied on the old behaviour. Two properties make the full correction a migration rather than a patch.
It revokes grants real deployments depend on. It does not only revoke the dangerous bucket writes — it also revokes ListBucket, GetBucketLocation and ListBucketMultipartUploads when granted through bucket/*. The evidence is upstream’s own test suite: eleven STS integration tests grant s3:ListBucket on bucket/* and then assert that listing works. If the project that wrote the server writes it that way, production policies do too.
It cuts both directions. The matcher builds the same resource string for Allow and Deny, so removing the slash tightens over-granting Allow statements and simultaneously loosens over-blocking Deny statements. An administrator who locked a bucket with Deny s3:* on bucket/* would silently lose that protection.
How the protected set was chosen
The scope was decided by one question: does reaching this action give the caller something its object-scoped grant does not already provide?
That question is the right one because of how the defect fires. Resource matching runs after action matching, so the bug only bites when the statement already grants the bucket-level action — which in practice means s3:*. The affected principal therefore already holds full read, write and delete over every object in the bucket. The relevant question is what reaching a bucket action adds to a caller that already controls all object data in the bucket.
Withheld from object-only grants (twelve actions):
| Action | Why it qualifies |
|---|---|
PutBucketPolicy, DeleteBucketPolicy |
Hand access to other principals, anonymous included, and can grant the caller bucket-level actions it was never given. Self-escalation and public exposure. |
PutBucketObjectLockConfiguration, PutBucketVersioning |
Defeat protections that exist precisely to stop a holder of write access from destroying data. |
PutReplicationConfiguration, PutLifecycleConfiguration |
Act under server credentials and keep acting after the caller’s access is revoked. |
DeleteBucket, ForceDeleteBucket |
Destroy the bucket entity and its configuration irreversibly. The reproduction in the upstream issue. |
PutBucketCors, DeleteBucketCors, PutBucketQOS, PutInventoryConfiguration |
No server behaviour is attached to these today — no handler at all, or a handler that returns NotImplemented after the authorization check. Withholding them costs nothing and covers them in advance. |
Deliberately not withheld, and asserted by a test so that adding one is a deliberate act with a visible cost rather than an edit to a list:
PutBucketTagging,PutBucketEncryption,PutBucketNotification. These are bucket-level writes and an earlier draft did withhold them. None gives the caller access it does not already hold — the harm is to the owner’s posture, not to the access boundary — while a tenant handeds3:*onbucket/*and told the bucket is theirs may quite reasonably tag it, set default encryption, or wire up event notifications. Low security gain against a real compatibility cost is the wrong trade for a maintenance release.CreateBucket. It targets a bucket that does not exist yet, so there is nothing to mutate or destroy, and provisioning flows commonly create a tenant’s bucket with that tenant’s ownbucket/*credentials.- The read/list family (
ListBucket,GetBucketLocation, the configuration reads). Breaking these is what got upstream’s own attempt reverted. They wait for a migration-gated release.
Only Allow statements are affected. Deny statements keep the historical resource string, so no bucket lock is ever weakened, and NotResource exclusions keep their full reach.
Monotonicity, and the claim that was wrong twice
All of the above rests on one property: this change may remove permissions and must never add one. That property was asserted twice from reasoning rather than from tests, and was false both times. Recording how is more useful than recording only the final state.
The first attempt let the withheld slash reach the NotResource match as well — and NotResource is an exclusion. An Allow s3:* NotResource bucket/* statement historically did not apply to bucket-level requests on that bucket; matching the exclusion against the bare bucket name made it stop matching, so the Allow it qualified grew, for exactly the writes being protected.
The second attempt fixed that and shipped saying the result was provably monotone. Testing against fixed-width wildcard patterns exposed a counterexample. Withholding the slash changes which string patterns are matched against, and a pattern can match "mybucket" without ever having matched "mybucket/". A fixed-width wildcard is the clean case:
? matches exactly one character. Against the nine-character "mybucket/" it does not match, so this statement never authorized the bucket-level write. Against the new eight-character "mybucket" it does, so the hardening granted something the buggy matcher refused.
On the protected path the matcher now requires both forms to match — the bare bucket name and the historical "bucket/". The result is an intersection with the historical decision, so it is monotone by construction: there is no pattern it can newly satisfy. mybucket* still grants (it matched both all along), mybucket/* is still withheld, and mybucke? is refused as before.
Evidence
The property is verified rather than argued. A decision corpus of 27,000 authorization outcomes — 15 resource patterns × 3 buckets × 5 object names × 20 actions × 6 statement forms — was generated against both the pre-hardening baseline and this release and compared entry by entry:
| Transition | Count |
|---|---|
false → true (broadening) |
0 |
true → false (narrowing) |
144 |
| unchanged | 26,856 |
Every one of the 144 narrowed outcomes falls inside the design intent, with nothing outside it: exactly the twelve protected actions; only the three Allow statement forms, with zero transitions for Deny, NotResource-excluded or deny-NotResource forms; only four object-only resource patterns; and only bucket-level requests, with object-level requests entirely untouched. 12 × 4 × 3 = 144, fully accounted for.
Regression coverage exists at both layers. In this repository, twelve matcher tests pin each direction, including an invariant test that every protected action really is bucket-only — ResetBucketReplicationState, despite its name, is an object action and stays out. In the server, three end-to-end tests drive the real handlers at the client, inline-session-policy and S3-router levels; all three fail against the pre-fix build and pass against this one.
What to change
You are affected only if a stored policy grants one of the twelve actions — or s3:* — on a resource pattern containing /, with no bare bucket ARN for the same bucket. The fix is to add the bare ARN alongside the object pattern:
That pairing is the conventional form, is what upstream’s own tests use, and worked before this release as well. Built-in canned policies are unaffected — readwrite, readonly, writeonly and diagnostics all use Resource: "*".
MINIO_API_LEGACY_BUCKET_RESOURCE_MATCH=on, read once at startup, restores the historical matching in full — both the over-granting and the over-blocking. It is a single global switch; per-action scoping is deferred.
Policy Condition-Key Lookup Order
getValuesByKey() previously looked up a policy condition key by its canonical MIME spelling (http.CanonicalHeaderKey) before trying the original name. The map it reads mixes values calculated by the server for the current request (SourceIp, SecureTransport, CurrentTime, username and others, stored under condition-key spellings) with HTTP headers supplied by the request (stored under canonical MIME spellings).
Checking the canonical spelling first allowed a client header to override a value calculated by the server.
For a MinIO server this is a policy bypass. The simplest example is s3:prefix: a Prefix request header could satisfy a home-directory prefix condition while the real ?prefix= query parameter still listed the entire bucket. The same path reached aws:SourceIp, aws:SecureTransport, aws:CurrentTime, aws:EpochTime, aws:username, aws:userid, aws:principaltype, aws:UserAgent, aws:groups, ldap:username, ldap:groups, jwt:groups, s3:versionid, s3:signatureversion, s3:signatureAge, s3:authType and s3:LocationConstraint. Anonymous bucket policies were directly exposed. SigV4 did not prevent the attack because a client can add a header that is not listed in SignedHeaders.
There was a second consequence: when the server stored a value under one spelling and the policy key resolved another, the wrong entry won. s3:object-lock-mode could resolve to the caller’s X-Amz-Object-Lock-Mode header rather than the retention mode the server would actually apply.
The fix reverses the lookup order: match the condition key’s exact name first, then use the canonical spelling only as a fallback for condition keys that genuinely name request headers, such as the s3:x-amz-* family. This ports minio/pkg#226 and adds regression coverage the upstream change did not carry.
At the library’s raw-map layer, if a producer stores one logical field under both the exact condition name and its canonical MIME name, the exact name now wins. This is a library lookup rule, not an S3 wire-protocol rule that says query parameters take precedence. The Silo server first normalizes condition values by their real source. For storage class and upload tagging, where both Header and query forms remain compatible, Header presence wins, including an empty value; query is only the fallback.
LDAP Connection Path
Three defects in connect(). Two were introduced by this fork in b0c08a7 and shipped in v3.6.2 and v3.6.3. Users of either release should upgrade promptly.
StartTLS was skipped when ServerInsecure was enabled. Upstream called StartTLS in an outer block controlled only by ServerStartTLS, so enabling both options created a plaintext connection and then upgraded it. b0c08a7 moved the call into an else branch, making StartTLS unreachable whenever ServerInsecure was true. The connection stayed plaintext and the following bind sent credentials over it. MinIO exposes MINIO_IDENTITY_LDAP_SERVER_INSECURE and MINIO_IDENTITY_LDAP_SERVER_STARTTLS independently and Validate() rejects no combination, so this state was reachable.
This release restores the upstream semantics: the two switches are additive, not mutually exclusive. ServerInsecure disables implicit ldaps://; ServerStartTLS still performs the upgrade. The exposure window is limited to v3.6.2 and v3.6.3.
A Config without a TLS section could panic on the ldaps:// path. After l.TLS.Clone() moved outside the StartTLS branch, ordinary ldaps:// connections also called it. Clone() returns nil for a nil receiver, but the next line assigned ServerName. The MinIO server always supplies TLS settings, but this is a library and mc also consumes it. The code now falls back to an empty tls.Config, matching what DialURL would have built.
StartTLS had no deadline. go-ldap only starts its request timer when requestTimeout > 0, while StartTLS itself has no timeout. A server that completed TCP setup and then stopped responding to the extension request could hold the connect goroutine forever. The timer is now armed before StartTLS.
A failed StartTLS leaked the connection. Inherited from upstream. Dial failures do not return a connection, making StartTLS failure the only connect() path that could return both a connection and an error. Callers only took ownership when the error was nil, leaving a socket behind for every login attempt against a server with a broken upgrade. The failure path now closes the connection and returns nil.
Other Fixes
- certs: file watchers were never stopped.
Manager.AddCertificate()registered twonotify.Watch()calls and stopped neither: if the second failed, the first leaked, and both survived until process exit after the manager closed.Certificate.Watch()andwatchFile()had the same problem. All four paths now usewatchDirSafe(), which returns a stop function invoked on errors andctx.Done(). This ports thecerts/part of minio/pkg#228. On Windows the function replaces filesystem notification with polling rather than using polling only as a failure fallback, so certificate reload can lag by onesymlinkReloadInterval(10 seconds). This fork has no Windows CI; that platform was only cross-compiled. - rng: reader subkeys came from a zeroed local variable.
init()read 32 bytes of entropy intor.tmpbut derived four subkeys from a same-named zeroed local, collapsing four per-block streams into one.Reset()andResetSize()then replayed the previous stream byte for byte. MinIO creates a new reader for eachrandreader.New()call and never resets it, so the practical server impact is limited; warp exposed the defect. This ports minio/pkg#230. - xtime:
DurationimplementedUnmarshalJSONbut notMarshalJSON. Encoding produced an integer number of nanoseconds while decoding unconditionally stripped the first and last byte and expected a quoted string, so neither direction could round-trip. It now encodes usingtime.Duration’s string form. This ports minio/pkg#242.
Compatibility Impact
- Twelve bucket-level write actions are no longer authorized through an object-only resource pattern. See What to change. Object access,
ListBucket,CreateBucket, bucket tagging, default encryption and event notification are all unaffected, as areDenystatements andNotResourceexclusions. - The minimum Go version moves from
1.26.1down to1.25.0. A patch number in thegodirective is a hard minimum for every consumer, not a record of the toolchain used to build the module. The conventional split is a language version on thegoline and a development version on a separatetoolchainline.1.25.0is what the dependency graph actually requires and what upstream declares. CI builds the complete test suite with Go 1.25 underGOTOOLCHAIN=local, so the minimum is proven rather than aspirational. - The JSON wire format of
xtime.Durationchanges from a nanosecond integer to a duration string such as"2h"or"30m". Persisted numeric values can no longer be read back. No such use was found in MinIO ormc: batch job definitions persist as YAML and the msgp path remains int64. - Deployments with both
ServerInsecureandServerStartTLSenabled whose LDAP server does not support StartTLS connected successfully in plaintext on v3.6.2/v3.6.3 and now fail to connect. That is the correct result, but it surfaces during connection rather than configuration validation. DisableServerStartTLSfor such a server. Policy.IsAllowedActionscan disagree with a direct decision for the twelve protected actions. It enumeratesSupportedActions, which includes thes3:*pattern itself, so the returned set can contains3:*— and therefore appear to permit a protected action — while the direct evaluation denies it. Nothing in the server calls it, and Console calls it with an empty bucket name, which never reaches the hardened branch. Recorded rather than changed, because altering a public API’s output in a maintenance release is the larger risk.
Divergence from Upstream v3.11.0
The version number follows upstream’s line and makes no claim of identical content. The measured delta, comparing action-string constants across policy/:
| Count | |
|---|---|
Upstream minio/pkg v3.11.0 |
291 |
silo-pkg v3.11.0 |
270 |
24 actions exist only upstream: six s3:*ObjectAnnotation* actions, five admin: actions (DistJobStatus, Get/SetBucketCompression, two TablesReplication*), and thirteen s3tables: actions covering function CRUD and tagging. These belong to the AIStor vocabulary this fork deliberately does not carry, because the community server does not implement them.
Three actions are named differently on each side. Upstream renamed and split these; this fork retains the earlier names:
silo-pkg v3.11.0 |
upstream minio/pkg v3.11.0 |
|---|---|
s3tables:TagResource |
s3tables:TagTable, s3tables:TagWarehouse |
s3tables:UntagResource |
s3tables:UntagTable, s3tables:UntagWarehouse |
s3tables:ListTagsForResource |
s3tables:ListTagsForTable, s3tables:ListTagsForWarehouse |
A policy naming any of these six action strings therefore validates on exactly one of the two. Nothing in the Silo server, mc or Console references them, so there is no impact inside this ecosystem — but a consumer swapping upstream v3.11.0 for this release should know the vocabulary is not interchangeable.
rng has no arm64 assembly. Upstream added rng/xor_arm64.{go,s} after this fork’s divergence point; this release falls back to the pure-Go xor_noasm.go path on arm64. The result is correct and cross-compiles cleanly, but slower than upstream on that architecture. It is a clean candidate for a future sync, being a pure performance change with no vocabulary entanglement.
Companion Server Behavior
- The condition-key change in this release must be paired with the server changes that reserve internal condition-key names and populate values by semantic source, as noted at the top.
s3:signatureAgeis exposed only after the SigV4 presigned-request verifier calculates it. A client-suppliedx-amz-signature-ageHeader is ignored on every other request type.s3:prefix,s3:delimiterands3:max-keyscome only from query parameters. Content hash, copy source, metadata directive, SSE and object-lock conditions come only from the corresponding headers. TheX-Amz-Content-Sha256query value consumed while verifying a presigned request does not become a policy condition.s3:x-amz-storage-classretains its compatible query form, as do request tags onPutObjectandCreateMultipartUpload. For both fields, Header presence wins and query is used only when the Header is absent.s3:ExistingObjectTag/*comes only from tags loaded from the stored object, so a request’s ownX-Amz-Taggingcan no longer impersonate existing object state.PutObject,CreateMultipartUploadandPutObjectTaggingbinds3:RequestObjectTag/*to the tag input those handlers consume. Other action paths retain the historicalX-Amz-TaggingHeader fallback for compatibility, so treat request-tag conditions as constraints only where the API actually consumes tags.aws:SourceIpis calculated from forwarding headers. Whether it is enforceable depends on the server’s trusted-proxy configuration; see the server’s own release notes forMINIO_API_TRUSTED_PROXIES.
Verification
Everything below was run against the tagged commit, with the working tree clean and the tag pointing at HEAD:
make test— golangci-lint plusgo test -race -tags kqueue ./..., all packages passing.go mod tidy -diffclean;gofmt -lempty;go vet ./...clean.- Cross-compilation for
linux/amd64,linux/arm64,darwin/arm64andwindows/amd64. govulncheck ./...— zero reachable vulnerabilities. One module-level notice remains, GO-2026-5932 inx/crypto/openpgp; that package is unmaintained, has no fixed version, and this repository does not import it.- Resolution from an empty module cache through the public proxy, confirming the release is fetchable as published.
- The 27,000-outcome authorization corpus described above.
Dependencies and Tooling
Dependency updates clear nine reachable findings previously reported by govulncheck: seven x/crypto/ssh issues reached through sftp, GO-2026-6061 in gRPC reached through etcd, and GO-2026-4945 in go-jose reached through oidc.
Five dependencies — minio-go, minio/mux, etcd client/v3, go-oidc and lestrrat-go/jwx — were deliberately not upgraded. MinIO consumes this module through replace, and Minimal Version Selection chooses the highest version in the entire graph, so upgrading them here would also pull the server forward. None has a reported vulnerability requiring that change.
All three workflows previously asked setup-go for a Go version lower than go.mod required and failed on the first Go command; they are now aligned. The linter also fetched an installer from the master branch and reinstalled it on every run. The URL and version are now pinned to v2.11.3, and a matching installed version skips the download.
Changes Deliberately Not Taken from Upstream
- AIStor policy vocabulary (Memory/cortex, Tables/Iceberg, KMS, compression and annotations) and the typed action-constant refactor, none of which the community server implements. This is the source of the action-vocabulary delta.
securityAuditAdmin, which grantsadmin:ExportIAMand therefore exposes every secret key despite what the name suggests.- rng AVX2/NEON assembly. Revisiting the arm64 half is noted above as a future sync candidate.
net.BandwidthBytesPerSec(declared but never read upstream),replicationAdminandDistJobStatusAction.- Two changes initially taken and removed after review: the
consolereadonlybuilt-in policy andGetAllGlobalCertificates. Neither has a consumer. Once operators bind a built-in policy name to users, withdrawing it is particularly unsafe: policy mappings persist by name, and an unresolved name merges into an empty policy that denies everything. Its inheritedadmin:CreateUserDeny also cannot be combined withiamAdmin. The certificate helper inventoried a cache the community server never populates. - Upstream’s golangci-lint
tooldirective, which would add roughly 200 linter dependencies to every downstream consumer’s module graph.
Deliberately Deferred
The general problem in minio/minio#20449 — that bucket/* still reaches ListBucket, GetBucketLocation, the configuration reads, CreateBucket and the three tenant-plausible writes — is not closed here. Closing it means revoking grants real deployments depend on, so it belongs to a release that carries a migration path.
What that release owes operators is more than a longer action list, because no one can enumerate every deployment’s stored policies — which puts a hard ceiling on any approach that picks the protected set by guessing. Three things raise it:
- A startup policy audit that walks stored policies and names each one whose meaning changes, in both the grant and the deny direction. It is read-only and can ship before the enforcement change rather than with it, turning an upgrade surprise into a pre-upgrade checklist.
- A denial that explains itself. When a request is refused because only an object-scoped grant matched, say so and name the compatibility switch. A break an operator can diagnose in thirty seconds costs an order of magnitude less than a silent one.
- A switch with a scope.
MINIO_API_LEGACY_BUCKET_RESOURCE_MATCHis all-or-nothing today, so an operator who needs one action back must reopen the self-escalation path along with it.
Related Commits
- d8b1fa7: fix(policy): settle the bucket-write hardening’s scope and monotonicity
- 1f97549: fix(policy): extend the bucket-write hardening to every bucket-only write
- 3c24ad1: fix(policy): withhold object-only grants from sensitive bucket writes
- da6a22a: docs: say what this fork is and how to depend on it
- 4055b2f: fix(xtime): marshal Duration as a duration string
- 13c26cd: fix(rng): initialize the reader subkeys from the seeded entropy
- 88b37ac: fix(certs): stop file watchers on every exit path
- 74dd36e: fix(ldap): keep StartTLS when ServerInsecure is also set
- 424c3d0: fix(ldap): close the connection when StartTLS fails
- 045d10f: fix(ldap): guard a nil TLS config and arm the StartTLS deadline
- 5c4bf50: fix(policy): prefer the exact key name over the canonical header form
- 802539f: chore(deps): refresh the dependency set and declare the real minimum Go
- e4ec64a: ci: build on the Go version go.mod requires, and prove the declared minimum
- 747d8b8: build: pin the golangci-lint installer and skip a matching install
24 - Silo 20260903 Released
Version date: 2026-09-03 · Published: 2026-09-04 · Version: RELEASE.2026-09-03T13-18-01Z · Previous release: RELEASE.2026-08-06T00-00-00Z
SILO 20260903 is the security and correctness release following the first fully rebranded Silo release. It tightens core server invariants: client headers do not create replication authority, browser origins cannot trigger bucket-metadata I/O before authentication, concurrent bucket-configuration writers cannot silently overwrite one another, and authorization and checksum behavior more closely match AWS S3.
This note consolidates the complete server, Console, shared-package, and client baseline delivered on 2026-09-03 relative to 20260806. Later fixes—including SN-2026-011—shipped in SILO 20260916, SILO 20261015, and SILO 20261018.
Highlights
- Replication trust is authorization-derived.
X-Minio-Source-Replication-Requestand related internal fields gain replication semantics only after signature verification, an exact marker check, ands3:ReplicateObjectors3:ReplicateDeleteauthorization. Untrusted fields are stripped after authentication so SigV4 remains valid. - Per-bucket CORS is complete.
PUT,GET, andDELETE ?corspersist a real bucket policy, override the server-wide fallback, and converge through site replication. The pre-authentication lookup is resident-only and remains fail-closed during startup and after a known metadata-load failure. - Bucket metadata updates are serialized. A shared
metadata.lockcovers all whole-record configuration writers, migrations, imports, site adoption, and healing.ForceCreateno longer erases existing configuration, and an Object Lock document always implies plainEnabledversioning. - Authorization matches the requested operation. Explicit version deletion requires
s3:DeleteObjectVersion; user and group enable/disable calls require the action matching the requested status; policy writes reject empty ARN namespaces. - Checksum behavior moves toward AWS S3. Upload parts can receive server-computed checksums, federated
UploadPartCopyreturns its checksum, multipart completion reportsChecksumType, invalid assertions use AWS-compatible failures, andCRC64NVMEwithCOMPOSITEis rejected. - SSE-C reads and copies authenticate the key consistently. Zero-byte objects and
GetObjectAttributesno longer bypass key validation; null-version rewrites and in-place key rotation no longer leave unreadable ciphertext or inconsistent checksum metadata. - The release was simplified before publication. Dead code and obsolete lint exemptions were removed, compatibility snapshots now track served surfaces instead of source-level noise, dependencies were pinned to reviewed revisions, and flaky timeout tests received a private random source.
- The client release is part of the same baseline. mcli 20260903 adds read-only checksum verification, fail-closed credential redaction, repaired JSON metrics and S3 Select behavior, strict policy writes, signed packages, provenance, and verified amd64/arm64 images.
Security fixes
The repository security ledger assigns stable local identifiers to five findings without CVEs. An SN- identifier is not a CVE. The complete threat models, compatibility effects, and operator actions are collected in SILO 20260903 Security Notes.
| ID | Affected surface | Fixed invariant | Operator impact |
|---|---|---|---|
SN-2026-006 |
SSE-C reads and copies of zero-byte objects | The supplied customer key is authenticated even when there is no payload block to decrypt | Wrong keys now return 403 AccessDenied; no change for correct keys |
SN-2026-007 |
GetObjectAttributes on SSE-C objects |
Object attributes require the customer key, except for an actually authorized replication peer | A bare replication marker cannot skip key authentication |
SN-2026-008 |
Internal replication fields across reads, writes, multipart, deletes, Snowball, and events | Replication semantics require authentication plus the matching replication permission | Ordinary clients carrying internal-looking headers keep ordinary S3 semantics |
SN-2026-009 |
Admin user and group status changes | Enable and disable are authorized against different actions | Split custom admin policies that previously granted only one action but relied on both |
SN-2026-010 |
DeleteObject / DeleteObjects with versionId |
Explicit version deletion requires s3:DeleteObjectVersion |
Review grants and add an explicit Deny where Deny s3:DeleteObject was intended to block permanent deletion |
All five defects were inherited from the archived upstream server lineage and affect every earlier SILO release. SN-2026-008 completes the receiver-wide repair begun by CVE-2026-34204.
The embedded Console v2.3.0 line also contains a trusted-proxy boundary, TLS verification repair, response and log redaction, and bounded WebSocket forwarding.
S3 correctness and interoperability
Multipart checksums
This release closes the multipart interoperability group reported in #46, #47, #48, and #50:
UploadPartcomputes the selected checksum when the client selects an algorithm at upload creation but omits a per-part checksum header.UploadPartCopyreturns the remote checksum through a legacy federated backend instead of dropping it.CompleteMultipartUploadincludesChecksumTypeand uses AWS-compatible error codes when a checksum is absent, malformed, contradictory, or unsupported.- Unknown checksum algorithms and the invalid
CRC64NVME+COMPOSITEcombination are rejected rather than silently canonicalized.
The fixes are deliberately layered. The wire parser rejects invalid declarations, the multipart state records the selected algorithm and type, and completion validates the final claim against that state. Ordinary clients that do not opt into additional checksums are unchanged.
The individual evidence records are server-computed UploadPart checksums, multipart completion error semantics, and ChecksumType propagation.
CopyObject and SSE-C
CopyObject now:
- calculates checksums over the logical object before optional compression;
- returns the checksum fields in its response;
- preserves transform state on metadata-only copies;
- decrypts source checksum metadata with the source key and rewrites it for the destination key;
- handles null-version copies in either pool direction without losing current-version metadata;
- re-encrypts an in-place SSE-C key rotation when the object layer must rewrite the data.
The zero-byte and attributes fixes remove two independent places where “no data was decrypted” had accidentally been treated as “the key was authenticated.” The new rule is explicit: access to SSE-C metadata or ciphertext requires a successfully unsealed object key, unless the request has already crossed the authorized replica boundary.
The CopyObject state matrix and live interoperability evidence are preserved in CopyObject SSE-C, checksum, and response correctness.
Listing and federation
- ListObjects shortcuts now return
NoSuchBucketfor a missing bucket even when a prefix selects an early-return path (#32, PR #37). - Federated
UploadPartCopypreserves the backend checksum in the public response (#72). - Site-replication status counts metadata operations per site instead of multiplying a group-wide result, and validates CORS as an independent configuration field.
The missing-bucket shortcut is documented in ListObjects must prove the bucket before it optimizes.
Per-bucket CORS and request trust
Bucket CORS is implemented end to end:
- the S3 XML type and rule matcher enforce the protocol grammar and multi-rule preflight behavior;
- bucket metadata stores the raw configuration and returns it byte-for-byte on
GET ?cors; - the outer middleware uses the bucket policy when present and the global origin setting only as fallback;
- site replication carries CORS in its own tombstone-aware last-writer-wins register;
- pre-authentication lookup never loads metadata or creates cache entries;
- startup, a known bucket-metadata load failure, deletion, refresh, and on-demand reload all update the fail-closed state explicitly.
The load-failure state is intentionally retained. A pre-signed URL authenticates itself without consulting bucket policy; if the server forgets that a real bucket’s CORS document failed to load, falling back to a permissive global policy would discard the only browser-origin boundary around that otherwise authorized request.
Replication-request hardening shares the same principle. The server authenticates the original request first because internal headers may be covered by SigV4, derives one private trust decision from the authenticated identity and permission, and only then sanitizes an untrusted request clone. The clone shares the original trailer map so streaming checksums remain visible. Snowball workers derive trust per entry rather than inheriting a request-wide privilege bit.
The full design and rejected alternatives are preserved in No I/O Before Auth, No Privilege From Headers.
Bucket metadata and Object Lock
Bucket configuration is stored as one .metadata.bin record but was previously updated through independent locks for policy, lifecycle, encryption, tags, quota, replication, Object Lock, and CORS. Two valid read-modify-write operations could therefore each succeed while the later save silently discarded the earlier field.
This release introduces one bounded metadata.lock transaction around every whole-record mutation. The same lock covers:
- normal configuration saves and deletes;
- legacy metadata migration and import;
- bucket creation,
ForceCreate, and site adoption; - healing and inconsistent-version repair;
- replication receive paths that merge changed fields.
The implementation does not turn the metadata system into a generic transaction framework. It keeps the existing record and parsers, centralizes only the serialization boundary, and narrows replicated updates to fields that actually changed.
Object Lock receives a second invariant. Once a valid lock configuration parses as enabled, versioning is normalized to a plain Enabled document; prefix exclusion and suspended states cannot survive. The decision is based on the parsed configuration, not byte equality with the smallest possible XML document, so a valid Default Retention rule cannot bypass it. Update, read-back, and disk-reload tests cover both malformed prior versioning shapes.
IAM, policy, and configuration behavior
- Explicit version deletion follows AWS
s3:DeleteObjectVersionauthorization while replication deletes retains3:ReplicateDelete. SetUserStatusandSetGroupStatusauthorize the requested target state instead of always checking the enable action.- New and updated named policies and service-account policies reject bare S3, S3 Tables, and KMS ARN namespace prefixes, their historical serialized forms, and statements that combine
ResourcewithNotResource. Stored policies continue to load. - Enabled legacy PostgreSQL or MySQL notification targets must supply a connection string. Startup now fails with a credential-free diagnostic instead of silently discarding all targets.
MINIO_CONFIG_ENV_FILEparsing preserves named targets, quoted values, and supported shell-like assignments without evaluating the file as shell code.
These changes are compatibility tightenings, not format migrations. Review the upgrade checklist before deploying.
Detailed records cover target-state user/group authorization, safe configuration environment-file parsing, and fail-fast legacy database notification migration.
Components
The 20260903 dependency and companion-release line is pinned as follows:
- Go 1.27.1
minio-go/v7: upstream-compatible pseudo-version ending in0e78d3f18efe; the temporarysilo-gofork is retired from the server graphmadmin-go/v3:v3.0.110silo-pkg/v3:v3.13.2, under its owngithub.com/pgsty/silo-pkg/v3module path- embedded Console:
v2.3.0, including the trusted-proxy, TLS, redaction, and WebSocket security work - bundled mcli:
RELEASE.2026-09-03T07-13-05Z, with themccompatibility alias preserved - Helm chart:
7.0.2; use the published server imagepgsty/silo:RELEASE.2026-09-03T13-18-01Zand the mcli imagepgsty/mc:RELEASE.2026-09-03T07-13-05Z
Tagged-source Helm note: the signed Server tag retains the provisional
RELEASE.2026-09-03T00-00-00Zvalue inhelm/silo. Post-release commit4c164907fcorrects the currentmainchart to the published tag. If you render Chart 7.0.2 directly from the tagged source archive, setimage.tag=RELEASE.2026-09-03T13-18-01Zexplicitly; binary and package artifacts are unaffected.
The server deliberately preserves the MinIO-compatible Go module and wire identifiers. Replacing an import target does not rename the public S3/Admin API, MINIO_* configuration, x-minio-* headers, /minio/* routes, or on-disk metadata.
Engineering cleanup
The final release pass removed complexity where it did not protect a compatibility or correctness boundary:
- removed dead encryption helpers, obsolete handler paths, and unused event-target functions;
- replaced a source-level exported-symbol inventory with the smaller served-route compatibility baseline;
- removed the
wait_pipelint exclusion and migrated togomodguard_v2; - consolidated CORS load-failure lifecycle changes behind two helpers instead of open-coding set operations in six paths;
- narrowed site-replication imports to changed metadata fields;
- gave dynamic-timeout tests their own random source so parallel tests cannot mutate a package-global seed;
- retained the shared metadata lock, CORS tombstones, two-level replication trust, and regression tests covering each reproduced failure.
The pre-release verification record, including fixes identified during release testing, is in SILO Server 20260903 Pre-release Review.
Upgrade checklist
Read Upgrading from RELEASE.2026-08-06 before changing production. At minimum:
- grant
s3:DeleteObjectVersionto identities that intentionally delete explicit versions; add its Deny next to anyDeny s3:DeleteObjectintended to prevent permanent deletion; - split custom admin grants for enable and disable operations;
- correct bare ARN prefixes before re-submitting policies;
- add connection strings to enabled legacy PostgreSQL/MySQL notification targets;
- verify applications that explicitly choose checksum algorithms or types;
- upgrade every member of a site-replication group before creating or changing per-bucket CORS;
- snapshot CORS configuration before rollback, because 20260806 does not understand it;
- switch every node of a distributed cluster to the same binary in one maintenance operation; mixed Silo binaries do not form a cluster.
The on-disk object and erasure formats are unchanged; distributed clusters require all nodes to run the same release.
Known issues and explicit deferrals
These items are not fixed by this release:
- Conditional delete (#10, PR #12).
DeleteObjectignores HTTPIf-MatchandDeleteObjectsignores each<Object><ETag>. Both delete unconditionally; see the design analysis (subsequently implemented in SILO 20261015). - Non-CORS multi-site configuration deletion (#77). A peer can restore a policy, SSE, tag, or quota configuration deleted on another site. Single-site deployments are unaffected. CORS is not affected because it uses a tombstone-aware register. Deployments relying on these replicated deletions must verify every site (subsequently fixed in SILO 20260916).
ListMultipartUploadsfilters (#79).prefixbehaves like an exact key match, whilemax-uploads,key-marker, anddelimiterare not fully honored; see the conformance analysis and 2026-10-18 decision (commit4733e1423).- Legacy federation
CopyObject(#99, #100). The legacy federated backend can ignore a requested checksum algorithm and rejects inline source objects. Deployments not using that backend are unaffected (subsequently fixed in SILO 20261015). - Mixed-version site replication. A 20260806 peer accepts but ignores bucket CORS and continues reporting a mismatch. Do not configure CORS until all sites are upgraded.
- Rollback of bucket CORS. 20260806 drops the unknown CORS field if it rewrites the bucket record. Export the configuration first and recreate it after upgrading again.
Open enhancement work such as ILM relocation (PR #60), broader SSE support (#61), operator discovery (#30), NATS target hot reload (#40), and Renovate (#20) is outside this release’s scope.
Verification
The complete local acceptance was run on ebac0ca73bbf251b070bb6df4d8005015841f901:
- full
cmdandinternaltest suites; go test -race ./cmd, passing in 365.448 seconds;- lint with 0 issues, the rebrand/compatibility guard, generated-file checks, and
govulncheck; make verifyacross FS, erasure, distributed erasure, erasure sets, multi-pool, and IPv6 multi-pool: 174 PASS / 0 FAIL.
One subsequent one-line fix, 84e1580a4, clears the CORS load-failure bit after a successful on-demand GetConfig reload. Later dependency work moves the toolchain to Go 1.27.1 and x/crypto 0.56.0. On exact release source 9b11dc9469e650815b775cb47b039610644f5da4, git diff --check, full cmd and internal tests, the complete cmd race suite, rebrand and generated-file guards, lint, govulncheck, and remote Go CI, VulnCheck, Test Release, and Release workflows pass.
The final functional acceptance used four Ubuntu 24.04 arm64 VMs with four XFS data disks each. It covered a coordinated 20260806 → 20260903 upgrade, rollback, and second upgrade; the complete mcli functional script over TLS; 1,004-object upload/download checksum comparison; one-node and quorum-loss behavior; single-drive reconstruction; and two-site replication of objects, versions, CORS, tags, quota, ILM, Object Lock, IAM, delete markers, and offline catch-up. The cluster finished with all 16 drives online and no unrecovered data mismatch.
Container publication exposed one delivery-only pin error after the binary/package Release was public: the first Docker workflow stopped before pushing any image because the tagged Dockerfile selected mcli 20260903 but retained the two 20260901 archive digests. Commits ce2326c94 and 9936a69d8 corrected those pins and added a narrow recovery mode that resolves them from the immutable, attested mcli checksum manifest. Recovery run 33847571968 then built, ran, SBOM-attested, and published both architectures from the original Server release archives. The classic release/latest manifest is sha256:b616a0cf8cb281e7e6bb3c9b1fb53875b4016a2878223925541c18f82d6c5ca3; distroless is sha256:2711cdc5bce0ada353a4871dcda60f48f46414fdd69a45c36c71f6b7ebe1aead.
The published server GitHub release identifies the exact tagged source and verified archives, packages, checksums, SBOMs, and provenance; its classic and distroless images publish matching amd64/arm64 manifests. The separately published mcli release is immutable, contains 19 verified assets, and publishes matching amd64/arm64 release and latest images. Shared package v3.13.2 and Console v2.3.0 retain their own release evidence.
Resolved issue ledger
The tracker was reconciled during release preparation. Core issues are closed; deliberately excluded work remains in its own open issue:
| Tracker | Resolution |
|---|---|
| #102 | Closed by merged PR #103: shared metadata.lock and complete writer/migration/adoption coverage; reproduction-first residual audit split to #105 |
| #58 | Closed by merged PR #104: explicit-version authorization with multi-delete context preservation and least-privilege replication tests; PR #59 closed as superseded |
| #46, #47, #48, #50 | Multipart checksum selection, computation, response, validation, and errors |
| #32, PR #37 | Missing-bucket listing shortcut returns NoSuchBucket |
| PR #57 | CompleteMultipartUploadResult.ChecksumType is present in the selected client/server stack |
Selected changes
1c9a2431fthrough13e6458d9: implement per-bucket CORS and site replication938603458through04b097fd9, plus Snowball follow-ups: authenticate replication semantics and remove pre-authentication metadata I/Of9f9fa6c9through32a1b81e4: reproduce and serialize cross-type bucket metadata updates3b5de82f5,21646eebd: enforce Object Lock’s versioning invariant, including retention-rule documentsf8b598f1dthroughd2d47a41f: align explicit-version delete authorizationb73581b05,474cd5801,74c97d005: authenticate SSE-C zero-byte and attributes reads7fea6d5a5,5d152416d,7e079ff05,d28885d0e: align multipart checksum behaviorc0e715977,e73436c99,ffb70eb37: repair CopyObject transforms, checksums, and SSE-C rotation84e1580a4: clear the load-failure guard after an on-demand metadata reload succeeds
Acknowledgments
This release incorporates reports and proposed fixes from community contributors as well as the fork’s security and compatibility audit. In particular, the checksum, missing-bucket listing, and explicit-version authorization work started from public issues and pull requests.
The complete authorship record remains in CONTRIBUTORS.md.
25 - Explicit Version Deletes Now Require DeleteObjectVersion
Starting in Server 20260903 (pgsty/silo#104, tracking issue #58), SILO maps object-delete authorization to the operation the request actually performs:
| Request | Required action |
|---|---|
No versionId |
s3:DeleteObject |
| Version UUID | s3:DeleteObjectVersion |
Explicit versionId=null |
s3:DeleteObjectVersion |
DeleteObjects |
Applied independently to each XML entry |
Previously, SILO required s3:DeleteObject for every delete operation and evaluated s3:DeleteObjectVersion only as an explicit-deny check. A principal holding only DeleteObject could permanently remove a specific historical version, whereas a least-privilege purge principal holding only DeleteObjectVersion was denied.
With this change, a DeleteObject-only principal can perform an unversioned delete or create a delete marker, but receives AccessDenied when targeting a version UUID or null version. A DeleteObjectVersion-only principal can permanently remove a named version, but cannot create a delete marker. Explicit denies and s3:versionid conditions retain normal policy precedence. Multi-object delete (DeleteObjects) evaluates each entry’s own VersionId so a query-string parameter cannot alter another entry’s condition value.
Replication compatibility
Bucket and site replication target policies do not need to add s3:DeleteObjectVersion. Authenticated replication requests establish delete trust through the exact internal replication marker and s3:ReplicateDelete, preserving the deployed s3:DeleteObject + s3:ReplicateDelete receiver contract. An explicit deny on s3:DeleteObjectVersion continues to block replicated version purges.
This separation keeps upgraded replication targets compatible with existing least-privilege policies without granting new standalone delete capabilities to ReplicateDelete-only credentials. Two-site regression tests verified that a target user holding the documented minimal policy (without DeleteObjectVersion) converges both permanent version deletes and delete markers.
Upgrade impact
- Review user, service-account, OPA, and external authorization policies that currently grant only
s3:DeleteObjectfor principals that runmc rm --version-id,mc rm --versions, Console delete-all-versions, or SDK deletes with an explicitversionId, and grants3:DeleteObjectVersionwhere needed. - External authorization plugins now receive a single
s3:DeleteObjectVersioncheck for ordinary named-version deletes instead of the former deny-only check followed bys3:DeleteObject. X-Minio-Force-Deleteprefix cleanup remains gated bys3:DeleteObject.- Wire and storage formats are unchanged.
See Object Deletion for the full permission matrix.
26 - mcli 20260903 Released
Published: 2026-09-03 · Version: RELEASE.2026-09-03T07-13-05Z · Source: a2ef95c0
Twenty-eight days after mcli 20260806, this release carries everything the client accumulated since. The toolchain moves to Go 1.27.1 and the shared dependency stack is aligned with the SILO family, with silo-pkg imported under its own module path so go.mod says what the binary is built from. The first genuinely new command since the fork arrives — mcli checksum verify, a read-only audit of stored S3 additional checksums against the bytes an endpoint actually returns. The panic that made --json admin prometheus metrics unusable is fixed, as is the response double-close that could panic after mcli sql had printed valid rows. Every path that can print a credential — --debug traces, admin trace, error messages, JSON error documents, checksum reports — was rewritten fail-closed. The release process changed as much as the code: artifacts come from a GPG-signed tag at the exact tip of main with green CI for that commit, ship LICENSE, NOTICE, and a generated CREDITS in every format, carry Sigstore build provenance, and land in an immutable GitHub release.
The release was built from a2ef95c0, the merge of pgsty/mc#27: 58 reachable commits, 50 of them non-merge commits, across 262 files (+12,742 / −6,566 lines) since RELEASE.2026-08-06T00-00-00Z. The intermediate 20260901 build remains available on GitHub, but this page treats 20260806 as the public documentation baseline and records the complete 20260903 result.
Behavior changes
- macOS 13 is the new floor. Go 1.27 links darwin binaries with a minimum OS version of 13.0; the darwin archives no longer run on macOS 12 and earlier.
mcli --json admin prometheus metricsworks again and returns metric families in deterministic name order. Plain-text metrics output is unchanged.pipewith empty standard input now performs a regular zero-byte PUT instead of a one-part multipart upload. The resulting ETag isd41d8cd98f00b204e9800998ecf8427erather than the<md5>-1form. Non-empty input is unchanged.- Policy writes are validated strictly on the client —
admin policy create,admin user svcacct add/set,idp ldap accesskey create/edit. A bare ARN such asarn:aws:s3:::is rejected (writearn:aws:s3:::*to mean every resource under that prefix); a named policy withoutVersionis rejected; an empty policy is rejected; mixing S3 and admin actions in one statement is rejected. The last three were already refused by the server, so only bare ARNs change what can be stored. Reads of existing policies remain permissive. Session policies for service accounts may still omitVersion, andadmin user svcacct set --policystill accepts an empty document — that is the way to clear an inline policy and return the account to its inherited one. - A malformed global flag before the command name is now an error.
mcli -H "bad" ls,--resolveand--limit-*values that fail to parse at the app level used to be silently ignored and the command ran without them. They now fail with the same message the command-level form produces, reported once on stderr — or as a JSON error document under--json— where the CLI library used to echo the text to stdout first. - Error messages no longer echo secrets.
alias setreports only the length requirement for a bad secret key;MC_CONFIG_ENV_FILEparse errors name a line number instead of repeating the line; SSE-C key errors withhold the key;admin config setmasks secret-bearingkey=valuepairs; remote replication targets are printed without credentials; a malformed--custom-headerentry is reported by position, never by value. - Support artifacts are private. Files written by
support diag,support profile,support perf,support inspect,admin cluster bucket export, andadmin cluster iam exportare created with mode0600, including the rotated backup of a previous file. They were0644or0666before. --debugshows less. AnAuthorizationheader keeps only its scheme (AWS4-HMAC-SHA256 **REDACTED**); tokens, cookies, API keys, SSE-C keys and every--custom-headervalue are redacted; 2xx response bodies are no longer dumped.admin traceoutput is redacted the same way in both its default and its verbose rendering: header values by name, query strings, bodies, error text and annotations.- The User-Agent reports
minio-go/v7.3.0(the SDK’s version constant) instead ofminio-go/v7.0.98.
Major Changes
- Go 1.27.1 across the module, container build stage, Linux/macOS/Windows CI, cross-builds, tidy checks, and vulnerability analysis. The Go-maintained module family is current:
x/net0.58.0,x/sys0.47.0,x/term0.45.0,x/text0.41.0,x/crypto0.56.0,x/mod0.40.0,x/sync0.22.0, andx/tools0.49.0.golangci-lintstays outside the product module graph and is installed independently at the pinned v2.13.1. - The SILO package under its own module path.
go.modnow requiresgithub.com/pgsty/silo-pkg/v3v3.13.2 directly. The client’s policy validation depends on helpers that exist only there —policy.ParseConfigStrictandpolicy.Resource.IsBareARN— and areplaceof upstreamminio/pkgwould not be inherited by projects consuming this module (pgsty/mc#12), so the requirement is declared honestly. Upstreamminio/pkgsurvives only as an indirect dependency ofcolorjson, held at v3.6.1 by the dependency-floor check, andgomodguardblocks the upstream import path from returning.minio-gomoves from v7.0.99 to the latest upstreamminio/minio-gomaster commit, represented byv7.3.1-0.20260828014306-0e78d3f18efe; the interimpgsty/silo-gofork is retired. The final refresh includes etcd 3.7.1,go-pretty6.8.3,klauspost/compress1.20.0, Prometheus client/common/procfs 1.24.1/0.71.0/0.22.0,grpc-gateway2.30.0, gRPC 1.83.2, Protobuf 1.36.12, and JWX 3.2.0.go-systemddeliberately stays at 22.6.0 through SILO’s portability pin — 22.7.0 does not compile on NetBSD. - Runtime version floors are guarded. A first dependency pass let the removal of development tools silently lower a dozen runtime selections — among them
shoenig/go-m1cpu, whose older releases crash during initialization on newer Apple Silicon when CGO is enabled — and was rejected.buildscripts/check-dependency-floorsnow compares every requirement ingo.modagainst the previousRELEASE.*tag and fails the build on a regression; the only permitted exception is thego-systemdpin. mcli checksum verify(design). Verifies stored S3 additional checksums — CRC32, CRC32C, CRC64NVME, SHA1, SHA256, of typeFULL_OBJECT— against the logical object bytes returned by the object API, for one object, a prefix with--recursive, all versions with--versionsor an exact--version-id, or a JSON Lines candidate manifest. It reads withIf-Matchon unversioned objects and re-stats afterwards, so a write that races the verification is reported asUNKNOWN_OBJECT_CHANGEDrather than as a mismatch. Each object yields one ofMATCH,MISMATCH,NO_CHECKSUM,WOULD_VERIFY(dry run), one of tenUNKNOWN_*classifications, or one of threeSKIPPED_*results; the summary carriesobjects, averifiedcount (MATCH+MISMATCH, the only results that actually recomputed a checksum), per-status counts andincomplete.--fail-onacceptsmismatch,unknown,no-checksum(fails when any object lacks a checksum or when nothing was verified at all),any(default: mismatches, unknowns, and objects skipped by--max-size), ornone.--reportwrites the same records to a new0600JSON Lines file. Results go to stdout whether or not stdout is a terminal, and with--jsonthey are compact JSON Lines outside a terminal — the non-TTY silence reported as pgsty/mc#5 is fixed. Dry runs,--max-workers,--max-size, time filters, and SSE-C through--enc-care supported. The command is verified against a real SILO server in CI.- Credential redaction, fail-closed. Earlier pattern-based redaction only caught expected credential shapes and let edge cases through: access keys containing a slash, an
Authorizationvalue whose first field is empty so the second one hides, non-hex signatures, secrets tucked into the credential scope orSignedHeaders,Proxy-Authorization, cookies,X-Api-Key, response trailers, a redirectLocationwith userinfo, a server that reflects request headers into an error body, a JSON-escaped secret inside--jsonerror output, or a short token that appears twice. The final design has three rules and no exceptions. AnAuthorization-class value keeps only its scheme. A header whose name looks like a secret, or that the caller supplied through--custom-header, is redacted without inspecting its content. Text the client did not generate itself — response bodies, server error messages, trace notes — is scanned for credential shapes and then scrubbed of every secret literal the process has learned. That last layer is backed by a process-wide secret registry: alias secret keys and session tokens,MC_HOST_*values, SSE-C keys in every encoding, tier and service-principal secrets, GCS credential files, LDAP passwords and the STS credentials they return, SUBNET tokens,--secret-key/--account-key/--api-keyflags, and secret-bearingkey=valuearguments are registered as they are read, before validation and before any network call. Occurrences are collected as intervals and merged before replacement; JSON error documents are scrubbed on the decoded value tree, string leaves only, so keys and compact non-terminal formatting survive. Over forty edge-case test vectors, a black-box test that runs the real client against an endpoint that reflects everything it receives, and live captures against a SILO server pin the result. admin traceredaction. Server-supplied header maps for other clients’ requests take a path the S3 tracer never touched. Both the human-readable and the JSON rendering, in the default and the verbose form, are now produced from redacted copies — the event is shared between them and never mutated. Query strings (a presigned URL’s signature, a session token in the first parameter), bodies, error text and annotations are scrubbed with the event’s own header values, so a token another client sent cannot reach privileged trace output. Headers are redacted by name: a--custom-headeranother client sent under a name that does not look like a secret is shown as the server saw it.
Fixes
- JSON metrics no longer panic.
mcli --json admin prometheus metricsfailed for both the v2 and v3 metrics APIs withpanic: Invalid name validation scheme requested: unsetin the 20260806 client.madmin-gobuilt the zero value of the Prometheus text parser, whichprometheus/common0.66 and newer reject. The client now constructs the parser with UTF-8 validation, returns ordinary parse errors instead of panicking, and emits families in name order. pipewith empty input performs a regular PUT; the one-part multipart upload it used to issue for zero bytes was needless and produced a multipart-style ETag.- RPM payload validation aligned with the signing script, so an RPM whose contents do not match the release metadata is refused before it is signed.
sqlno longer panics after printing its rows. The SDK’s select reader closes the HTTP response itself on every terminal event, and the client closed it a second time; on a zstd-compressed response the two drains raced and about two in five queries against a SILO server ended inpanic: invalid memory addresswith exit status2after the rows had already been written (pgsty/mc#23). The client now cancels the request and drains the result pipe instead of closing the response again; 820 consecutive queries on a live server ran without a panic where the previous client failed 21 of 50.
Hardening
- Operator-produced files are no longer world-readable.
support diagwarned that its archive may contain sensitive environment information and then wrote it0666; the sharedmoveFilehelper relaxed every0600temporary file to0644on its way to the final name, and a0644left by an earlier client was carried into the rotated backup even when source permissions were preserved. Every support artifact is now created0600and chmodded before the first byte is written. Test_FullSuitefails on panic. The functional suite recovered from panics to run cleanup and then reported success; a panic anywhere in the suite, including in the binary build, was invisible to CI.- A leftover
play/alias in the new command’s help — the only file in the repository still advertising MinIO’s operated demo alias — is replaced, and the brand gate now rejects bare aliases in command examples, not only hostnames. - AGPL notices completed on six fork-authored files that carried only the first half of the license text.
Engineering and Delivery
- Third-party notices in every artifact.
CREDITSis generated bybuildscripts/gen-credits.shfrom the union of the runtime modules linked into all six GoReleaser targets and is compared in CI so a dependency change cannot leave it stale.LICENSE,NOTICE, andCREDITSship in the GoReleaser archives, the DEB/RPM/APK packages, and every container image under/licenses/. - CI runs on
main.GoandCrosscompileran only on pull requests, so the merge commit — the one that gets tagged — was never tested; both now run on every push tomain. Their concurrency group was keyed ongithub.head_ref, which is empty outside pull requests, so every manual run cancelled every other; it is now keyed on the pull request number with the ref as fallback.VulnCheckalso runs weekly, because a fork of an archived upstream can go weeks without a commit. The functional suite in CI now targets SILORELEASE.2026-08-06T00-00-00Z. - Release only from signed tags. The artifact workflow has no manual trigger; artifacts come from a pushed
RELEASE.*tag, and the event SHA, the commit the tag points to, and the checkout must all agree.verify-release-tag.shrequires an annotated tag with a valid OpenPGP signature from the expected key — fingerprint and key location come from repository variables rather than from the tree the tag points at, and the imported key must match the expected fingerprint.check-release-commit.shrequires the tagged commit to be the exact tip oforigin/mainand demands a successful push-to-mainrun of four workflows for that SHA, matched by workflow file path; pull request and manual runs are not evidence.check-release-state.sh(pgsty/mc#6) runs serially per tag, replaces exactly one existing Draft from scratch, refuses a published release, and re-checks for a single Draft before packages are uploaded. Every one of these decisions lives in a shell script with fixture coverage; the fakeghin the tests pins both the verdicts and the arguments passed, verbatim. - Provenance and immutability. Archives, checksums, DEB and APK packages carry Sigstore build provenance (
gh attestation verify <file> --repo pgsty/mc). RPMs are excluded on purpose:sign-release-rpms.shrewrites their bytes with a detached GPG signature from key9592A7BC7A682E7333376E09E7935D8DB9BD8B20while the release is still a Draft, and an attestation made earlier would name a digest no published RPM has. GitHub releases are immutable once published; fixing an asset means a new tag. Thepgsty/mccontainer image is built only from attested archives, aftergh attestation verifybinds each one torelease.yml@refs/tags/<tag>, the release commit digest, GitHub-hosted runners, its own filename and its SHA-256; the unpacked binary’s ELF machine type must match the archive’s architecture; each per-architecture image is pulled, run, and checked for the tag before the manifest is assembled, andlatestis written only from the two-architecture manifest. The UBI base image is pinned by digest and kept current by Dependabot. - Repository governance matches a single-maintainer project. Direct pushes to
mainare allowed; the stale pull-request ruleset and its exact-name status-check coupling were removed after this release. Release integrity does not depend on that branch rule: the artifact gate still requires successful push-to-mainGo, Crosscompile, VulnCheck, and Test Release runs for the exact tagged commit.RELEASE.*tags remain administrator-created and immutable; secret scanning, push protection, Dependabot alerts, and security updates remain enabled. - Verified before and after tagging. On exact source
a2ef95c035d9ae7cc01469a63926900f1786f9e2, tidy/verify, dependency floors, unit tests, the full race suite, lint, actionlint, source-awaregovulncheck, nine standard cross-builds, a clean-cache Go proxy/sumdb build, 172 functional checks against a temporary SILO server, a 47-command black-box comparison with the published 20260901 binary, and seven additional cross targets all passed. All four exact-SHA main workflows and both publication workflows passed. The immutable public release contains 19 assets; archives and sidecars were checksum-verified, all 11 attested subjects were bound to the tag and source commit, both RPMs were GPG-verified, and thepgsty/mcrelease andlatestimages were independently confirmed as matching amd64/arm64 manifests. The source scan reports no reachable vulnerability.
Compatibility
The full --help tree of this release was diffed against the 20260806 binary: the only difference is the new checksum command. Side by side on the same server, head, cat, get, cp, mirror, find, share, sql, stat, and ls produce identical output and JSON field sets; twenty real-world policy shapes are accepted or rejected identically except for bare ARNs; admin policy info --json is byte-identical. The mc command name and the mcli package name; the ~/.mc / ~/.mcli configuration directories; the github.com/minio/mc module path and all import paths; MC_* environment variables; protocol headers (x-minio-*) and the minio-go user-agent prefix; the minio tier type; the .part.minio transfer suffix; the minio-job scrape job name; and the package formats, asset naming, and YYYYMMDDHHMMSS.0.0 version scheme are unchanged. The client remains compatible with MinIO servers and other S3-compatible endpoints. Projects that consume this module as a Go dependency now resolve github.com/pgsty/silo-pkg/v3 directly and need no replace of their own for it.
Known issues and boundaries
checksum verifyreportsUNKNOWN_READ_ERROR, notMISMATCH, when the server’s own integrity checks refuse a read — SILO’s bitrot protection rejects a corrupted shard before the object API returns it. AMISMATCHmeans the stored checksum describes different bytes than the object API returns, which is the historical write-path defect the command exists to find; anUNKNOWNis never reported as aMATCH.- Access key IDs are redacted wherever they appear inside a credential structure, but a server that reflects a bare access key into an error message will still have it printed: it doubles as the user identifier in legitimate messages. Secrets shorter than three characters are not registered. When the secret key equals the access key — the default
minioadmin:minioadmin— the identifier is redacted from error text too, because the string is the secret. --jsonerror documents list the keys ofcause.erroralphabetically rather than in struct order; the keys and values are unchanged.mcli sqlreports a failed query on stderr but still exits0(pgsty/mc#25); per-object errors are reported without failing the run so a recursive query can continue. Unchanged from upstream.govulnchecktraces GO-2026-5932 in the indirect dependencyx/crypto/openpgp; no fixed version exists and the client does not call it.- JWX 3.2 enforces JWT
critprocessing more strictly, as required by the standard. The terminal-width refresh can also adjust alignment for Indic or ZWJ-heavy text; neither change affected the tested command surface. - minio/mc#5139 continues to apply:
mirror --remove --watchcan delete a live object from the target when a non-current version of it is removed from the source. - Quality debt that does not block the release — archive reproducibility, test-tool portability, deferred coverage of KMS / SSE-S3 / multi-site replication — is tracked in pgsty/mc#21.
Related Commits
- 98a8b0f: build: prepare the 20260903 dependency release
- 6686b64: chore(deps): align the SILO Go dependency stack
- 423490d: fix: stop JSON metrics from panicking
- 14b05e6: build: generate and package third-party notices
- e938cea: fix: align RPM payload signing validation
- 8158605: release: adopt Silo Go SDK v7.3.1
- d6ba236: ci: allow manual release workflow runs
- b6b2655: ci: release an existing tag on dispatch
- 70a2950: fix: preserve embeddable pkg compatibility
- aa07b1c: feat: add read-only object checksum verification
- 1b39423: fix: preserve checksum output in pipelines
- a31a42b: docs: record checksum verify output contract
- 2121081: fix: validate policy writes strictly
- 712bf3a: fix: use regular PUT for empty pipe input
- 9a3805d: ci: make release retries tag-idempotent
- 7bc9843: test: make report permissions portable
- 2d5abc2: chore: complete the AGPL notice on fork-authored files
- f5c5dc9: build: import the SILO package under its own module path
- a8e7eb4: ci: test main on push and stop dispatch runs cancelling each other
- b75d28b: fix: stop checksum verify help from advertising the play alias
- c05da7b: chore: drop configuration for bots that are not installed
- a29b455: fix: stop –debug from printing credentials
- 08244df: fix: keep secrets out of error messages and traces
- 30a7ea0: fix: stop writing support artifacts world-readable
- 14bd724: test: fail the full suite when it panics
- 4abbb3a: feat: expose how much checksum verify actually verified
- f484d0d: ci: make a release prove its commit, and pin what it builds from
- 2c33a4e: chore: ignore the local consumer-module review scratch directory
- bdc742a: fix: close the remaining –debug credential leaks
- 3998392: fix: withhold SSE-C keys and credential URLs the parser rejects
- c1358c4: fix: force 0600 on rotated support artifacts too
- 45e5144: fix: fail –fail-on no-checksum when nothing was verified
- 3674933: ci: make the release gate provable and its claims true
- 8b7b2e9: test: exercise checksum verify against a real server
- 4b36b0a: fix: make credential redaction fail-closed and scrub final output
- a6c14da: ci: verify build provenance before repackaging archives into images
- 915cfd9: fix: reject a malformed app-level global flag instead of ignoring it
- 81e2767: fix: rebuild SigV4 traces from known fields and widen secret scrubbing
- 625713c: fix: keep only the scheme of a signed header and scrub JSON structurally
- ec12b64: fix: redact admin trace output and register every credential ingress
- 1445e88: ci: release only from a signed tag at the tip of main, verify exact provenance
- f188939: fix: close final credential and release verification gaps
- f5b88dd: fix: redact the default admin trace and stop the shape sweep mangling prose
- 59989f8: fix: let svcacct set clear a policy again, report bad global flags once
- d38412a: fix: classify an SSE-C refusal with a key as a read error, keep policy and size messages clear
- b291b53: fix: register a URL password as written, keep listing parameters visible in traces
- 9dfca8f: test: drive the SSE-C refusal classification through the verify path
- 76b2e89: fix: keep an unquoted DSN password whole and match auth schemes case-insensitively
- 328efee: fix: register the password before the first separator of an unquoted DSN payload too
- 7057705: fix: avoid double-closing S3 Select responses
27 - mcli 20260806 Released
Published: 2026-08-06 · Version: RELEASE.2026-08-06T00-00-00Z
Two days after mcli 20260804, this release completes the client’s transition to the Silo identity. It is deliberately a pure rebranding and lockdown release: --version and --help now present the Silo client, every remaining path to MinIO’s SUBNET service is disabled at build time, the embedded vendor encryption key is removed from the diagnostics tooling, and the contribution policy moves to no-CLA with a mandatory DCO sign-off. There are no dependency changes and no protocol changes in this cycle — go.mod is byte-for-byte identical to 20260804 — so the regression surface is confined to text, command gating, and CI.
Behavior changes
Every path that previously reached MinIO SUBNET is now disabled at build time and cannot be re-enabled at runtime:
mcli license register,mcli support upload,mcli support proxy set,mcli support callhome enable, and the online-renewal form ofmcli license update ALIASprint a stable notice — “MinIO SUBNET services (registration, licensing, uploads) are disabled in this Silo build of mc; diagnostics remain available locally.” — and always exit1. Drop these calls from scripts. The file-basedmcli license update ALIAS license.keystill works, with the license parsed offline against the bundled public key.mcli support diag/perf/profile/inspectalways operate in local (airgap) mode: reports, profiles, and inspect archives are written to local files and nothing is uploaded anywhere. The--airgapflag is still accepted for compatibility and is effectively always on. SUBNET registration is no longer a prerequisite for any of them.mcli support callhome disable|status,mcli support proxy show|remove,mcli license info, andmcli license unregisterkeep working — they only read or clear local and server-side configuration.- Fresh configurations no longer seed the
playalias pointing at MinIO’s public demo cluster; the defaults are nowlocal,s3, andgcs. Existing configuration files are never modified, and legacy-config migration still recognizes the historical entries. mcli --versiongains an identity line (“Silo object storage client, based on MinIO technology”) and a second copyright line. The first line’s machine-readable format is unchanged, so scripts parsing it are unaffected.
Major Changes
- Silo identity across the CLI: the client introduces itself as “Silo client for object storage and filesystems”. Roughly 220 help texts were reworked: usage lines that refer to the managed server now say “Silo/MinIO server”, example aliases moved from
myminio/playtomysilo, example LDAP DNs moved todc=example,dc=com, and example tier names toSILOTIER-*. Factual references stay factual: theminiotier type, protocol headers, and third-party interop mentions are untouched. - SUBNET disabled at build time: connectivity is compiled out behind a single guard, and the one HTTP choke point that every SUBNET request funnels through refuses with the stable error above. Command entry points gate early, diagnostics force local mode, and the AGPL license notice shown by
mcli license infono longer carries a commercial-subscription pitch. A dedicated regression suite (cmd/subnet-disabled_test.go) pins all of this, so an upstream merge cannot silently reconnect anything. - Governance — no CLA, DCO required: contributions are accepted inbound=outbound under AGPL-3.0-or-later; contributors keep their copyright, and the maintainers collect no rights beyond the project license. Every commit must carry a
Signed-off-bytrailer, enforced by a new CI workflow that matches the trailer against the commit author’s email and exempts only GitHub-issued bot addresses.CONTRIBUTING.md, the PR template, and both READMEs document the policy, and the code-of-conduct contact now points at the fork’s maintainer. - Dual copyright attribution: runtime output and help now credit both lineages —
Copyright (c) 2015-2025 MinIO, Inc.andCopyright (c) 2025-2026 PGSTY— with source builds computing the end year dynamically.NOTICEstates the fork relationship, and the non-affiliation with MinIO, Inc., explicitly. - Release line renamed to
main: workflow branch filters, documentation, and contributor instructions now targetmain; the legacymasterreferences are gone.
Hardening
- Vendor encryption key removed:
mcli support inspectused to fall back to encrypting its output with an embedded MinIO RSA public key whenever no key was supplied — producing archives only the vendor could decrypt. The embedded key is gone: inspect now relies on the server-generated per-request key that is printed to the caller (or an operator-supplied key), and any encrypted-upload path with no configured recipient fails closed instead of silently borrowing a third-party key. Diagnostic output an operator produces is now always decryptable by that operator. - Brand-policy gate:
buildscripts/check-branding.shruns inmake verifiersand in CI. It fails the build if MinIO-operated endpoints, commercial upsell URLs, the upstream product identity, or any embeddedMII…public key reappear in the command tree — while explicitly allowlisting the preserved compatibility identifiers (environment variables, protocol headers, module path, legacy-migration defaults, and original copyright headers).
Engineering and Delivery
- CI moved to the Node 24 Actions line:
actions/checkoutv7,actions/setup-gov7,goreleaser-actionv7, and the Docker action family — all still pinned to commit SHAs, with dependabot keeping the pins current. - Functional tests run against controlled servers only: the suite defaults to a local server (
localhost:9000) instead of MinIO’s public demo cluster, and CI downloads the pinned SILO server releaseRELEASE.2026-08-04T00-00-00Zfrompgsty/silo, verified by SHA-256, before running the suite. - Zero dependency changes: no module updates this cycle; the 20260804 security baseline (Go 1.26.5, zero known reachable vulnerabilities) carries over unchanged.
- Pre-release verification: verification covered the 245-file diff, brand/compatibility grep sweeps, call-graph checks confirming that no command or flag combination can reach
subnet.min.io/play.min.io/dl.min.io, and smoke tests confirming every disabled path returns its stable error with exit status1.
Compatibility
Everything scripts and integrations depend on is deliberately unchanged: the mc command name and the mcli package/binary name; the ~/.mc / ~/.mcli configuration directories (derived from the invoked name); the github.com/minio/mc module path and all import paths; MC_* environment variables; protocol headers (x-minio-*) and the minio-go SDK user-agent prefix; the minio tier type; the .part.minio transfer suffix; the minio-job Prometheus scrape job name; and the package formats, asset naming, and YYYYMMDDHHMMSS.0.0 version scheme. The client remains fully compatible with MinIO servers and other S3-compatible endpoints.
Known issues
The mcli watch regression flagged in the 20260804 notes is resolved on the server side: the fix shipped in SILO 20260804, and this client’s CI now runs the functional suite — including watch — against exactly that release. Pair mcli with SILO server 20260804 or newer to receive bucket events; older published servers remain affected.
Unfixed upstream defects continue to apply, most seriously minio/mc#5139: mirror --remove --watch can delete a live object from the target when a non-current version of it is removed from the source. Exercise caution combining --remove --watch on versioned buckets.
Related Commits
- 8a883ca: ci: move the branch filters to main and fetch the server from pgsty/silo
- 8c304dd: ci: move the pinned actions onto the Node 24 runtime
- 02b1c11: docs: name the release line main, not master
- 810bbd2: ci: pin the functional-test server to a release whose watch API works
- d145647: fix: disable SUBNET connectivity and licensing upsell paths
- 5061c4f: rebrand: adopt Silo identity in CLI help and examples
- c7f7706: docs: align governance files and package metadata with the fork
- 65c71b2: test: default functional tests to a local server and add brand gate
- c62a64d: fix: credit both MinIO and PGSTY in copyright notices
- d205f88: docs: adopt no-CLA plus DCO contribution policy
- 95326ce: docs: add related-projects table and polish contribution wording
- d2c0db7: fix: remove the vendor encryption key and close the proxy-set path
- 0c6704d: fix: repair a link and help text damaged by the brand sweep
28 - Silo 20260806 Released
Version: RELEASE.2026-08-06T00-00-00Z · Commit: 3be10fcc1a44f6620ded0bd303461f9d688cca23
SILO 20260806 is the first release published under the Silo name. The previous release, 20260804, was the last one delivered as pgsty/minio; this release completes the cutover to github.com/pgsty/silo and renames every delivery surface — binary, packages, container images, systemd unit, Helm chart — while deliberately preserving every wire and configuration surface a MinIO deployment depends on. On top of the rename it adds native health checking (silo healthcheck), a single-binary distroless container image pilot, complete license-compliance materials in every artifact, and a release pipeline gated on compatibility snapshots and build provenance.
The release covers 28 commits after RELEASE.2026-08-04T00-00-00Z, changing 396 files with 27,188 insertions and 19,561 deletions. It passed a six-phase pre-release acceptance, including a real four-node TLS cluster migration from MinIO to Silo — with byte-verified data integrity, maintenance-gated rolling restarts, fault injection, and a full rollback rehearsal.
Highlights
- The rebrand is complete, and compatibility is the contract. Repository, binary (
/usr/bin/silo), packages (silorpm/deb/apk), images (docker.io/pgsty/silo), and service (silo.service) are renamed; the S3 and admin APIs,/minio/*routes,MINIO_*environment variables,x-minio-*headers, on-disk.minio.sysformat, and Go module paths are all preserved and frozen by a CI compatibility guard. - Native health checking:
silo healthcheck [live|ready|cluster|cluster-read]probes the server’s own health API with correct exit codes, decoded quorum diagnostics, TLS auto-detection, and a--maintenancepre-drain gate — no shell,curl, ormcrequired in the container. - Distroless image pilot:
pgsty/silo:<RELEASE>-distrolessships exactly one program — thesilobinary — ongcr.io/distroless/static, with an exec-formHEALTHCHECKbaked in and/datacreated writable in the image layer. - The classic image does not change behavior: same entrypoint, same bundled tools,
mc ready localkeeps working, and noHEALTHCHECKwas added to it. It now bundlesmcli20260806. - Compliance completed: LICENSE and NOTICE ship in every package and image, CREDITS is regenerated from the actually-linked module set (291 modules) and guarded in CI, and the project adopts a no-CLA, DCO-based contribution policy.
- Components refreshed: embedded SILO Console 2.1.1,
silo-pkg3.11.0,mcli20260806, Go 1.26.5. - Provenance-gated releases: container images are built only from published, checksum- and attestation-verified release archives; image SBOMs and provenance attestations now cover the distroless variant too.
The rename
What changed, and what deliberately did not:
| Renamed (delivery surface) | Preserved (compatibility surface) |
|---|---|
Repository: github.com/pgsty/silo (main branch) |
S3 API, admin API, and request signing behavior |
Binary: /usr/bin/silo |
/minio/* routes, including /minio/health/* and metrics |
Packages: silo-*.rpm, silo_*.deb, silo_*.apk |
MINIO_* environment variables and x-minio-* headers |
Images: docker.io/pgsty/silo (+ -distroless) |
On-disk format (.minio.sys), erasure coding, versioning |
Unit: silo.service (conflicts with, and supersedes, minio.service) |
Go module and import paths (github.com/minio/...) |
Default config dir: ~/.silo (falls back to an existing ~/.minio) |
mc compatibility alias for the bundled mcli |
The server presents its own identity — silo --version reports the AGPL-3.0 license, MinIO’s 2015-2025 copyright, PGSTY’s modification copyright, and the “based on MinIO technology” attribution — and every inherited connection to MinIO-operated services (the update feed and its signing key, SUBNET, telemetry) is severed rather than redirected. The container entrypoint translates the legacy minio argv token, so docker run pgsty/silo minio server /data keeps working.
A snapshot-based rebrand guard runs in CI: it fails on any drift, in either direction, across 334 route literals, 437 environment tokens, 84 headers, and 9,014 exported symbols.
Native health checking
The server binary can now probe its own health endpoints, which makes container health checks possible without any second binary — and is what the distroless image relies on:
- The check vocabulary maps 1:1 onto
/minio/health/<path>;live(the default) answers “is this process serving,”readyadds KMS/etcd reachability when configured, and theclusterpair evaluates write/read quorum across every erasure set. - Exit codes are
0(healthy) and1(anything else) — never the Docker-reserved2. One diagnostic line decodes the server’sx-minio-server-statusand quorum headers fordocker inspect;--jsonemits a machine-readable verdict. - The probe target is derived the way the server derives its own listen address:
--address/MINIO_ADDRESS, with HTTPS auto-detected frompublic.crt+private.keyin the certs directory, or overridden wholesale with--url/MINIO_HEALTHCHECK_URL. The environment form exists because a probe process cannot see the server’s command line — if the server’s address or TLS comes from CLI arguments, one environment variable redirects the baked-in probe. silo healthcheck --maintenance clusteranswers the pre-drain question: exit0means the node can be taken down without losing HA; HTTP 412 (exit1) means it cannot.- Certificate verification is skipped, matching the kubelet’s documented behavior for HTTPS probes, and the transport ignores
HTTP_PROXYso loopback probes never route through a proxy.
Kubernetes needs none of this — kubelet httpGet probes hit /minio/health/live and /minio/health/ready from outside the container — and the cluster checks should stay out of per-container probes: they reflect cluster-wide quorum, not one process. The full design rationale, including verified endpoint semantics, is recorded in the health-check design note.
Distroless image pilot
Alongside the classic image, this release publishes a distroless variant: pgsty/silo:RELEASE.2026-08-06T00-00-00Z-distroless, plus a rolling distroless tag.
- Base is
gcr.io/distroless/static-debian12: CA certificates, tzdata,/tmp, and an/etc/passwdwith anonroot(65532) entry — no shell, no package manager, no libc. On top of it, exactly one program:/usr/bin/silo(plus the license set under/licenses/). The image is 128 MB versus the classic 199 MB. - The binary is the
ENTRYPOINT; an exec-formHEALTHCHECKrunningsilo healthcheck readyis baked in (interval 30s, timeout 10s, start-period 2m, retries 3), so Compose users get workingdepends_on: condition: service_healthywith zero configuration. /datais created in the image layer, world-writable — there is no entrypoint left to repair volume ownership at runtime, and this is what makes every privilege mode work,--userincluded. This fixes, for the distroless variant, the non-root failure documented in #55.- Not supported in this variant: the deprecated
MINIO_USERNAME/MINIO_GROUPNAMEprivilege-drop path (use--useror KubernetesrunAsUser),docker exec <c> shdebugging (use ephemeral-container tooling), and in-imagemc(use the releasedmclior the client image). - TLS: mount certificates at
/tmp/.silo/certs(the container’s default certs directory) and both the server and the baked-in probe derive HTTPS from the same location; for CLI-configured servers, setMINIO_HEALTHCHECK_URL.
The classic image remains the default and is unchanged. If the pilot proves out, the distroless variant becomes the recommended image later; the decision record lives in the design note above.
Container images
The classic image was diffed field by field against pgsty/minio:RELEASE.2026-08-04T00-00-00Z: entrypoint, exposed ports, volumes, working directory, user, and (absent) health-check configuration are identical. Exactly three differences exist, all deliberate: Cmd is ["silo"] instead of ["minio"], the upstream update-verification key variable MINIO_UPDATE_MINISIGN_PUBKEY is removed (updates through upstream channels are permanently disabled), and HOME=/tmp is declared to match the entrypoint’s writable-home guarantee.
The bundled client is upgraded to mcli RELEASE.2026-08-06T00-00-00Z (with the mc alias preserved), pinned by per-architecture SHA-256 digests and verified against the published checksums at build time. Interoperability of the released mcli 20260806 against this server — multipart, versioning, presigned URLs, metadata/tags, user and policy administration — was verified as part of release acceptance.
Helm chart
The chart ships as silo 7.0.1, preserving rendered resource identity with the legacy chart across a simulated upgrade (verified by the migration guard over 7 rendered resources). Its default image tag now points at this release — docker.io/pgsty/silo is a fresh repository, so the inherited default could never have pulled. The chart still ships no liveness/readiness/startup probes; adding them is planned, and documented, in the design note’s follow-up phase.
Packaging and migration
RPM, DEB, and APK packages install exactly six files: /usr/bin/silo, silo.service, a sysusers definition (creating the silo system user), /etc/default/silo (marked config/noreplace), LICENSE, and NOTICE. RPMs are GPG-signed with the PGSTY maintainer key (9592A7BC 7A682E73 33376E09 E7935D8D B9BD8B20). RPM and DEB now carry a unified, PGDG-style 1PGSTY release segment — silo-<version>-1PGSTY.<arch>.rpm and silo_<version>-1PGSTY_<arch>.deb — replacing the inherited bare -1 on RPM and the missing revision on DEB; APK names stay bare because Alpine pkgrel admits only -r<integer>.
silo.service is designed for takeover: Type=notify (readiness is signaled by the server itself), Conflicts=minio.service + After=minio.service (starting Silo stops a running MinIO unit), and two environment files — /etc/default/minio is read first and /etc/default/silo overrides it — so an existing MinIO configuration is inherited without editing. For existing deployments whose data is owned by the minio user, the documented drop-in keeps ownership untouched:
Distributed migrations must switch all nodes together.
Cluster bootstrap verifies that every node runs the same binary (by checksum). A mixed cluster — some nodes on Silo, some still on MinIO — does not form: the new node stays in activating, logging Expected Silo binary checksum ... seen: ... and Waiting for at least 1 remote servers with valid configuration, indefinitely. Stop MinIO on all nodes, then start Silo on all nodes (near-simultaneously). Once every node runs Silo, rolling restarts work normally — gate each one with silo healthcheck --maintenance cluster.
Migration troubleshooting, from the acceptance run: if Silo starts as the packaged silo user against a deployment whose TLS certificates live under the minio user’s home, it fails with HTTPS specified in endpoints, but no TLS certificate is found and restart-loops until the systemd start limit — the legacy-user drop-in above is the fix. Keep the MinIO package and unit installed (disabled) during the migration window: the rollback path — stop Silo, start MinIO — was rehearsed and reads all data written during the Silo window, because the migration touches neither data ownership nor format.
Components and dependencies
- SILO Console 2.1.1 — the embedded console, selected from
pgsty/silo-consolewhile preserving thegithub.com/minio/consoleimport path. silo-pkg3.11.0 — retains the policy/LDAP/certificate fixes including the LDAP-over-TLS repair tracked in #15.mcli20260806 — bundled in the image and released separately; see its release notes.- Go 1.26.5 — toolchain unchanged from 20260804.
Build, CI, and release pipeline
- Compatibility as a CI gate: the rebrand guard snapshots routes, environment tokens, headers, metrics, storage/policy identifiers, and exported symbols, and fails on any unreviewed drift; companion scripts assert the delivery surface (binary path, unit contents, image layout) and that no live upstream endpoint remains in runtime code.
- Release-image gate: every release-pipeline run builds both container images and asserts, among others: the distroless
HEALTHCHECKsurvives into the image config (it is a Docker extension outside the OCI spec),/dataships world-writable, no shell and no/usr/bin/minioexist, Docker’s health state turns healthy from the baked probe alone, and SIGTERM still stops the server gracefully as root and as--user 1001:1001. - Provenance chain: images are built from the published release archives after checksum verification and
gh attestation verifyagainst the exact tag; per-architecture SBOMs and provenance attestations are pushed for the classic and distroless images; the distroless health-check gate runs before the multi-arch manifests are promoted. - Workflow runtime moved to Node 24 across CI actions.
Compatibility and upgrade notes
- Package upgrades are a takeover, not an in-place update. Install
silo, keep/etc/default/minioas is (it is inherited), enablesilo.service; starting it stopsminio.servicevia the conflict relation. Data is untouched. - Keep data ownership stable with the legacy-user drop-in above; do not chown storage or move certificates during migration.
- Distributed clusters: full-stop switchover only. See the warning above — mixed Silo/MinIO nodes do not form a cluster.
- Container users: the image is now
docker.io/pgsty/silo;docker.io/pgsty/miniostays frozen at 20260804 as an archive. The classic image’s behavior is unchanged — includingmc ready localhealth checks — and the distroless variant is strictly opt-in. - Distroless differences are deliberate: no shell, no in-image
mc, noMINIO_USERNAMEpath; health is native; servers configured via CLI arguments needMINIO_HEALTHCHECK_URLfor the baked-in probe. - Helm users: chart 7.0.1’s defaults now pull this release; override
image.tagexplicitly if you pin versions. - Known and unchanged: the classic image still does not create
/datain the layer, so fully non-rootdocker runagainst a Docker-managed volume fails as before (#55, fixed in the distroless variant); the inherited Postgres/MySQL legacy notification-migration limitation from the 20260804 notes still applies (#53). - Pair with
mcli20260806 for the client side; older clients continue to work over the unchanged wire protocol.
Verification
This release was verified in stages, each with recorded evidence:
- unit and end-to-end matrices for the health-check command: target derivation and precedence (flag/env/derived), real-TLS auto-detection, exit-code contract, JSON schema, timeout bounds, usage errors;
- cluster-semantics verification on a four-node cluster: with 2 of 4 nodes stopped,
clusterreports 503 withwrite-quorum=5whilecluster-readandlivestay 200 — the write/read quorum split observed live, matching the erasure math; - image acceptance: the classic image diffed field-by-field against the 20260804 baseline; the distroless image asserted down to file inventory, exact health-check configuration, and root/non-root/TLS/env-override runtime scenarios;
- a six-phase pre-release acceptance concluding in a real migration: a Pigsty-deployed four-node TLS MinIO 20260804 cluster (16 drives, EC:4) was migrated to Silo via the packaged takeover path — reference data (multipart, versioned, tagged objects) read back byte-identical, four maintenance-gated rolling restarts,
kill -9fault injection with the load balancer serving 23/24 continuous IO rounds (the only failure in the kill second), Prometheus metrics continuity, and a full rollback to MinIO and back, proving the migration reversible.
Validation boundaries
External LDAP/OIDC/KMS/etcd services were not tested against live endpoints in this release run; amd64 packages were cross-built and payload-checked; Windows and Intel macOS targets were cross-compiled only.
Artifacts
- GitHub release
RELEASE.2026-08-06T00-00-00Zatpgsty/silo, with checksummed platform archives, provenance attestations, and RPM/DEB/APK packages (GPG-signed RPMs); docker.io/pgsty/silo:RELEASE.2026-08-06T00-00-00Zandlatest;docker.io/pgsty/silo:RELEASE.2026-08-06T00-00-00Z-distrolessanddistroless— published on demand from the finished release;- companion releases:
mcli20260806,silo-pkg3.11.0, embedded SILO Console 2.1.1; - design record: Native Health Checks and the Distroless Image.
Selected changes
15def34dc,77bdc4c0c: drop upstream delivery residue; present Silo identity and close inherited upstream services15ab10833: rename the delivery artifacts to silo and complete the package payload30749911b: ship the silo binary in the image and translate the legacy argv commande071bb77e: replace the minio chart with a silo chart that preserves identitybd8df5166: gate the rebrand on compatibility, packaging, and provenance evidence6613c2a3c: pin the external test fixtures and run the suites against the silo binaryfd2ca1c6d,c46b16ec6,c47733abc,f1c77d5a2: cut over to pgsty/silo and main; document the archived branch6740e6978: move the workflow actions onto the Node 24 runtimeb57275be3: adopt the no-CLA plus DCO policy and fix copyright terms62717d7bf,a6d6d9b02: update the embedded Console to 2.1.0, then 2.1.16bd9cf77e: regenerate CREDITS from the linked module set and guard it in CI219670d31: ship LICENSE and NOTICE in every package and image2ff594f4b: add the nativesilo healthchecksubcommand4c34d2309: add the distroless image variant as a pilotb6d47b739,9462cce16: healthcheck and distroless hardening; lint cleanup16b78eb4e: bundle mcli 20260806 and point the Helm defaults at this release062a91bee: pin the CREDITS module closure to the shipped linux target467931455: unify the rpm and deb release segment as1PGSTYb14ea22aa: match checksum manifest entries exactly in the image publish lane3be10fcc1: add a manual finalize lane refreshing SBOMs and checksums for signed Draft packages
Acknowledgments
Four contributors have code merged into this fork, and the Git history carries their authorship: @ZouhairCharef patched CVE-2026-34986 in go-jose (#18), @mfredenhagen patched CVE-2026-39883 in OpenTelemetry (#19), @pinginfo implemented Flush on trackingResponseWriter to repair bucket notification streaming (#34), and @waterkip repointed the documentation links to the Silo portal (#41).
A first release under a new name is also the right moment to thank everyone who has filed issues against this fork — bug reports, compatibility findings, and proposals alike, resolved and still open:
@mosesdd (#1), @Xavier-777 (#2, #17), @jiadzh (#3), @TLINDEN (#4), @AntonOfTheWoods (#5), @zylpsrs (#6), @nsanitate (#7), @makinikm (#9), @magicxor (#10), @spaceg00se-r (#11, #14), @heroes1412 (#13), @vampywiz17 (#15), @davinkevin (#20), @chalukyaj (#30), @cbornet (#31, #32), @jvasile (#33), @Kesavaambati (#35), @redfoxfox (#38), @kuldeep-link11 (#39, #40), @meesudzu (#42), @pmezhuev (#43), and @kh0mka (#51).
Several of this release’s headline items trace directly back to those reports: the bundled-client guarantee to #4 and #9, the LDAP-over-TLS repair to #15, the completed package payload to #33, GPG-signed RPMs to #43, the migration guide to #42, and the distroless /data fix to #55.
Pull requests still in flight deserve a mention too. @davinkevin’s distroless image PR (#21) anticipated this release’s pilot months in advance — the shipped variant supersedes that PR with the native health check built in, but the direction was proposed there first. Conformance PRs from @magicxor (#12) and @ycjlin (#37) are queued for review immediately after this release.
Everyone who has contributed to this fork is recorded in CONTRIBUTORS.md, which is now the project’s attribution record — GitHub generates no contributor graph for forks.
29 - Silo 20260804 Released
Version: RELEASE.2026-08-04T00-00-00Z · Commit: d88f46ccee345a9c2fabe2d221d9a9e56bc11aec
SILO 20260804 is a security, correctness, and release-engineering update to the pgsty/minio community fork. It completes the internode storage-containment work begun with CVE-2026-42600, prevents request-controlled values from impersonating server-calculated S3/IAM policy conditions, restores streaming flush behavior, fixes several multipart and versioning edge cases, hardens notification configuration migration, moves the build baseline to Go 1.26.5, and connects the server to the SILO-maintained Console, shared package, and mcli releases. The release pipeline was rebuilt to produce reproducible binaries and GPG-signed packages.
The release covers 50 commits after the pre-2026-06-18 baseline, changing 155 files with 9,241 insertions and 981 deletions. Every change was reviewed against the tagged commit and verified on macOS ARM64 and Linux AMD64, with GitHub CI green on the released HEAD.
Highlights
- Internode containment completed: validates storage-REST message bodies, storage Grid frames, and peer-S3 Grid requests at the storage boundary, closing the remaining path, volume, erasure-metadata, panic, and unbounded-allocation defects left after removing
ReadMultiple. - S3/IAM decisions now use effective values: client input can no longer shadow internal condition values; request tags and existing-object tags are separated;
s3:signatureAgeis confined to verified presigned requests; ands3:versionidfollows the version the server actually acts on. - Bucket and object resources are separated: twelve sensitive bucket-level writes are no longer authorized through an object-only
bucket/*resource pattern. A documented compatibility switch is available for migration. - Multipart compatibility and correctness improved: full-object checksum completion works without per-part checksums when the protocol permits it, zero-length multipart checksums are preserved, and duplicate part numbers are rejected instead of assembling duplicated data.
- Streaming reliability restored:
trackingResponseWriternow implementsFlushcorrectly and records implicit HTTP 200 responses, repairingmcli watch, bucket-notification listeners, and S3 Select keep-alives affected by the inherited regression documented in the 20260618 release. - Notification configuration hardened: NATS and AMQP keys used by parsers and legacy migration are registered and round-trip correctly; libpq connection parameters are quoted safely; invalid-key errors no longer echo secret values.
- Reproducible, signed release pipeline: binaries no longer embed the build machine’s paths, packages install under the canonical systemd path, and RPMs are GPG-signed. The container entrypoint now shuts down gracefully on every privilege path.
- Release baseline refreshed: Go 1.26.5,
klauspost/compress1.18.7, Apache Thrift 0.24.0, SILO Console 2.0.0,silo-pkg3.11.0, andmcli20260804.
Security Hardening
Internode storage and Grid containment — SN-2026-002
Removing the obsolete ReadMultiple endpoint in 20260618 closed one reachable path but did not close the underlying defect class. Storage-REST request bodies and Grid RPC frames do not pass through the HTTP query-validation middleware, and peer-S3 RPCs can bypass the storage-REST wrapper entirely.
This release moves containment to the storage boundary and validates every caller-controlled path, volume, erasure parameter, part size, shard length, and allocation length before use. The fixes include:
- reject traversal on both path and volume axes, including Windows volume-root aliases;
- cover peer-S3 bucket RPCs that reach drives without the storage-REST wrapper;
- reject zero or unusable data/parity/block-size combinations before shard arithmetic;
- reject negative part sizes and truncated shards instead of reporting them healthy;
- cap storage-REST
ReadFileallocations at 5 GiB; - bound other allocations derived from internode declarations;
- contain panics in deadline-bounded storage work without blocking the caller;
- preserve
ReadPartserrors across keep-alive responses and avoid the empty-part trace panic.
These routes require cluster-root or internode credentials and are registered only in distributed-erasure deployments. Single-node S3 behavior is unchanged. See Internode Path Containment Audit for the protocol-surface analysis.
Effective policy-condition values — SN-2026-003
The policy condition map historically mixed values calculated by the server with raw request entries. A client-controlled spelling could therefore shadow or synthesize an internal condition value. SILO 20260804 pairs silo-pkg 3.11.0’s exact-key lookup rule with server-side source normalization:
- internal condition names cannot be supplied as arbitrary client values;
s3:prefix,s3:delimiter, ands3:max-keyscome from their effective query inputs;- header-backed
x-amz-*conditions do not accept unrelated query substitutes; - when storage class or upload tagging supports both forms, an explicitly present Header wins, including an empty Header;
s3:ExistingObjectTag/*comes only from stored object metadata;s3:RequestObjectTag/*is bound to the tag input consumed by the relevant operation;s3:signatureAgeis exposed only after verified SigV4 presigned authentication calculates it;s3:versionidis absent when no version is named and is rebound perDeleteObjectsentry to the effective resolved version.
The version-ID behavior closes the fail-open trap that a superficial “omit empty values” fix would have created for Multi-Delete. See Absent Is Not Empty.
Bucket/object resource boundary — SN-2026-004
The IAM matcher used to append a slash to a bucket-level request, allowing an object-only resource such as arn:aws:s3:::bucket/* to authorize selected bucket-level operations. This release withholds twelve sensitive writes from that pattern on Allow statements:
PutBucketPolicy, DeleteBucketPolicy, PutBucketObjectLockConfiguration, PutBucketVersioning, PutReplicationConfiguration, PutBucketLifecycle, DeleteBucket, ForceDeleteBucket, PutBucketCors, DeleteBucketCors, PutBucketQOS, and PutInventoryConfiguration.
Deny and NotResource behavior is unchanged. Read/list operations, CreateBucket, bucket tagging, default encryption, and notification configuration remain compatible. Built-in policies use Resource: "*" and are not affected.
Policy migration required for custom bucket grants
If a custom policy grants one of the twelve actions — often through s3:* — using only arn:aws:s3:::bucket/*, add the bare bucket ARN:
MINIO_API_LEGACY_BUCKET_RESOURCE_MATCH=on restores the historical matcher while policies are migrated. It also restores the historical over-grant, so use it only as a temporary rollback control.
Trusted client-address boundary
MINIO_API_TRUSTED_PROXIES provides an enforceable, opt-in boundary for aws:SourceIp, audit remotehost, event notification Host, and the client address shown by mcli admin trace:
- set it to an address/CIDR list to trust forwarding headers only from those peers and walk forwarding chains from right to left;
- set it to
noneto ignore all forwarding headers; - leave it unset to preserve historical behavior exactly.
The old _MINIO_API_XFF_HEADER=off switch still suppresses only X-Forwarded-For; it does not protect against X-Real-IP or RFC 7239 Forwarded. If IP-based policy is part of your security boundary, configure trusted proxies explicitly and prevent direct access to the S3 API port. Multi-node deployments should allow their own node addresses. See Client Source Address Trust.
S3 and Storage Correctness
Multipart upload
- CompleteMultipartUpload accepts the S3 full-object checksum mode when the completed request supplies no per-part checksums and the upload metadata does not require them.
- The checksum of a zero-length multipart object is retained instead of being discarded as empty metadata.
- Part numbers must be strictly increasing. Duplicate entries such as
[1,1]now returnInvalidPartOrderinstead of consuming the upload and assembling the same part twice. Legal part lists with gaps or a non-1 start remain accepted. See Duplicate Part Numbers.
Object reads and buffer ownership
- erasure reads again pool buffers only where ownership permits reuse;
- update downloads return caller-owned buffers instead of exposing data that can be overwritten after return;
- the old HTTP streaming helpers orphaned by
ReadMultipleremoval are deleted after reference and platform-tag checks.
HTTP response tracking and S3 Select
trackingResponseWriter.Flush()delegates to the underlying flusher and commits the response state correctly;- the first implicit write records HTTP 200, preserving audit and metric accuracy;
- S3 Select tests no longer race a client parser against response-body ownership;
- CSV, JSON, and Parquet selection paths remain covered, including range/error and keep-alive behavior.
The inherited silent-flush regression called out in SILO 20260618 is therefore fixed in this release.
IAM, Versioning, and Audit Behavior
- DeleteObject and each entry in DeleteObjects evaluate
s3:versionidagainst the effective version selected by the server. - Request tags can no longer impersonate existing-object tags during policy evaluation.
- The
merrstag is restored when dangling-object deletion records are emitted, preserving the intended audit classification. - Bucket-policy and IAM paths share the hardened condition-source rules while retaining their established S3 routing and error behavior.
Notification Configuration
- registers the NATS
user_credentials,nkey_seed, andtls_handshake_firstkeys read by the parser; - separates the legacy NATS environment-variable spelling from the stored config key;
- repairs NATS migration round trips and the AMQP
immediate/internalmapping; - adds a mechanical audit that compares keys read and written by notification code with each subsystem’s registered schema;
- quotes libpq connection-string parameters so whitespace, quotes, and backslashes retain their intended value;
- prevents invalid-key diagnostics from echoing secret values.
See Notify Keyspace Registration.
Known legacy migration limitation
The inherited Postgres and MySQL legacy migration functions still write the unregistered host, port, username, password, and database fields. A migrated configuration can therefore fail validation on the next load, and notification target loading is fail-fast across subsystems. This predates 20260804, but upgrades from pre-connection-string database notification configurations must be reviewed and converted before restart. The stored password field may contain a plaintext database password.
Components and Dependencies
- Go 1.26.5: includes security fixes in
crypto/tlsandosplus compiler, runtime, networking, and syscall corrections. klauspost/compress1.18.7: refreshes the compression stack used by object and archive paths.- Apache Thrift 0.24.0: updates the dependency compiled through Parquet support.
- go-systemd 22.6.0: deliberately retained instead of 22.7.0 because the later version introduced a NetBSD clock dependency incompatible with the supported cross-build matrix.
- SILO Console 2.0.0: the embedded console is selected from
pgsty/silo-consolewhile preserving the compatiblegithub.com/minio/consoleimport path. silo-pkg3.11.0: provides the companion policy, LDAP, certificate, RNG, and time-format fixes while preserving thegithub.com/minio/pkg/v3module path.mcli20260804: the embedded client comes frompgsty/mc; release images expose it asmcliand keep themccompatibility alias.
See the companion release notes for silo-pkg 3.11.0, mcli 20260804, and SILO Console 2.0.0.
Build, CI, and Packaging
This release rebuilt the release pipeline for reproducibility and supply-chain integrity:
- Graceful container shutdown on every path. The entrypoint’s custom UID/GID branches now
execinto the server so it runs as PID 1 and receivesSIGTERMdirectly; previously those branches left an intermediate shell as PID 1 and the server was killed at the container stop timeout. A CI smoke test builds the release runtime image and asserts graceful shutdown on both the default and drop-privilege paths. - Reproducible binaries. Release binaries no longer embed the build machine’s
GOPATH/GOROOT, so-trimpathholds and a third party rebuilding the tag gets matching bytes. The published Linux binary contains no build-host path. - Hardened release workflow. The release tag is passed through the environment and whitelisted rather than spliced into the shell, the build is checked out at the tag being released, and an untracked shadow GoReleaser config that could publish or move
latestout of band was removed. - Honest gates. CI gates build, vet, unit tests, lint, generation drift, race tests, and cross-compilation; the cross-compile matrix is aligned to the exact set of published targets; and the lint and dependency-install steps now fail on real errors instead of masking them.
- Signed, canonical packages. RPM, DEB, and APK packages are produced with nFPM under the PGSTY identity, the systemd unit installs at
/usr/lib/systemd/system/minio.servicewithType=notify, and RPMs are GPG-signed offline with the PGSTY maintainer key (fingerprint9592A7BC 7A682E73 33376E09 E7935D8D B9BD8B20). - Separate archive and image publication steps. GoReleaser produces the platform archives, checksums, and packages; the multi-architecture container image is built and published from the finished release artifacts.
Compatibility and Upgrade Notes
- Keep every node on one release during a cluster rollout. Internode validation changed across storage-REST and Grid surfaces; mixed binaries were not production-tested.
- Audit custom IAM policies. Add the bare bucket ARN for the twelve protected bucket writes. Use
MINIO_API_LEGACY_BUCKET_RESOURCE_MATCH=ononly as a temporary migration control. - Configure client-address trust deliberately. If
aws:SourceIpor audit attribution matters, setMINIO_API_TRUSTED_PROXIESand close direct network paths around the proxy. - Review legacy database notification settings. Convert Postgres/MySQL host/user/password fields to the supported connection-string format before restart.
- Expect duplicate multipart completion entries to fail. Clients sending the same part number more than once now receive
InvalidPartOrderinstead of a corrupted successful object. - Use the matching
mcli. The 20260804 client disables self-update and must be upgraded through packages or GitHub Releases;mcli updateremains as a compatibility command but exits non-zero. - RPM users can enable signature verification. Packages are signed with the maintainer key above; import it before enabling
gpgcheckfor the SILO packages.
Verification
Verification against the tagged commit covered:
git diff --check, gofmt, module verification, and YAML/shell syntax;go build ./...,go vet ./..., project lint, andgovulncheck ./...;- full
go test ./..., the complete race suite, and repeated race tests over storage, policy, notification, HTTP tracking, and S3 Select changes; - generator idempotence plus deliberate stale-source and untracked-output counterexamples;
- cross-compilation across every published target;
- Linux AMD64 native full tests, targeted race tests, live S3/
mclismoke tests (create/upload/download/copy, range, versioning, delete markers, health checks, graceful shutdown, restart persistence), and systemd notify behavior; - release-artifact verification: GitHub CI green on the released HEAD, reproducible binaries with no build-host path, the systemd unit installed at
/usr/lib/systemd/system, and RPM signatures validated withrpmkeys --checksig.
govulncheck found no vulnerability reachable from the Server or mcli code. One module-level notice remains for the unmaintained golang.org/x/crypto/openpgp package (GO-2026-5932); that package is not imported into these binaries.
Validation boundaries
Native execution was run on Linux AMD64 and macOS ARM64; Windows, Intel macOS, and Linux ARM64 targets were cross-compiled. External LDAP, OIDC, KMS, STS, Postgres, MySQL, NATS, and AMQP services were not exercised against live endpoints in this test run.
Artifacts
- GitHub release
RELEASE.2026-08-04T00-00-00Zwith checksummed platform archives for Linux, Darwin, and Windows on amd64 and arm64; - RPM, DEB, and APK packages under the PGSTY identity, with GPG-signed RPMs;
docker.io/pgsty/minio:RELEASE.2026-08-04T00-00-00Zand the release-selectedlatesttag, published on demand from the release;- matching SILO Console 2.0.0,
silo-pkg3.11.0, andmcli20260804 references.
Selected Changes
ca7baa670,80e8eaa42,b6f70ab08: validate internode paths, erasure metadata, and allocation sizesa36fd8fff: contain panics in deadline-bounded storage work2f55347f7: bind S3/IAM policy conditions to effective request values744a9dcd7: binds3:versionidto the effective object version97b7d2804: enforce the bucket/object resource boundaryfe6dc4780: add the trusted-proxy client-address boundary22c1e41fd: reject duplicate multipart part numbersc8590413f,3e14733f1: restore full-object and zero-length multipart checksum behavior8069a32ac,65795ee1f: restore response commit and streaming flush semantics162ded343,0c14d8151: repair notification key registration and libpq quoting924717926,89d346bf5: restore safe buffer pooling and returned-buffer ownership3b8a55dee: exec into the dropped-privilege process so signals reach the server2ca4971d9: stop stamping the build machine’s paths into the binary4c185d5a6,e064b5555: harden the release workflow and remove the shadow configaa5139369: install the systemd unit under/usr/lib11d79fddc,ca674a696,021110b45,d88f46cce: gate build, vet, tests, lint, generation, race, and cross-compilation, and smoke-test the release image
30 - mcli 20260804 Released
Published: 2026-08-04 · Version: RELEASE.2026-08-04T00-00-00Z
This is the first release of the pgsty/mc community fork since 20260417. It fixes a credential leak in debug logging, severs every remaining connection between the client and upstream release channels, moves containers and packages onto artifacts this fork builds itself, and migrates packaging from MinIO’s pkger to standard nFPM — with GPG-signed RPMs for the first time.
Upstream minio/mc was archived in July 2026. Its final commit, 77f82e18, is exactly this fork’s base, and upstream never cut a release containing it — so this build is strictly newer than any official mc binary ever published.
Behavior change
mcli update self-update is disabled in this fork. The command remains for script compatibility and still accepts its original arguments, but it no longer contacts the network or replaces its own binary; it prints an explicit notice and always exits with status 1. Upstream mc update exited 0 when already up to date, so drop the call from any script that treats a non-zero exit as failure. Upgrade through the Pigsty package repository or GitHub Releases.
The automatic version check that ran against upstream release feeds on every invocation has also been removed entirely. The MC_UPDATE and MINIO_UPDATE environment variables are no longer consulted.
Major Changes
- Self-update disabled, upstream release channels severed: the
minio/selfupdateandaead.dev/minisigndependencies and all binary-replacement logic are gone, along with the update notifier and the FIPS/non-FIPS update paths. Theupdatecommand survives as a compatibility shell, and the runtime helpers (Docker / DCOS / Kubernetes / source-build detection) moved to a dedicatedcmd/runtime-info.go. The client previously reached out to upstream release feeds on every invocation to print an upgrade hint; there is now no outbound release probing at all. - Containers and artifacts fully localized: the default image is built from the checked-out fork source, and hotfix binaries are copied from the local build context — no upstream prebuilt binaries are downloaded. The upstream publishing files
Dockerfile.release,Dockerfile.release.old_cpu, anddocker-buildx.shwere removed, and the obsolete MinIO hotfix upload target is disabled. - Packaging migrated to nFPM: replaced MinIO’s
pkgerwith standard nFPM. Artifact layout and install path are unchanged (/usr/local/bin/mcli, package namemcli,YYYYMMDDHHMMSS.0.0version scheme), but the vendor is now PGSTY, the license uses the SPDX identifierAGPL-3.0-or-later, and the DebianSectionmoved from empty toutils. - RPMs are now GPG-signed: RPMs are signed offline with the maintainer key (fingerprint
9592A7BC7A682E7333376E09E7935D8DB9BD8B20). All package metadata is asserted before signing, and the signature is re-verified with checksums regenerated afterwards. DEB and APK packages carry no package-level signature; their trust anchor lives at the repository layer. - Build provenance hardened: every previously published binary was stamped by the Go toolchain as built from a modified working tree (
vcs.modified=true), which broke the link between an artifact and its Git tag. This release fixes that and adds an enforcing check to both the release and test pipelines, so every binary is traceable to an exact commit.
Security Fixes
- SUBNET credentials redacted in debug logs: with
--debugenabled, SUBNET HTTP exchanges are printed in full. Previously theapi-key/api_keyquery parameters, authentication headers, and response bodies all reached the log in clear text — and SUBNET’s authentication and registration endpoints return API keys, licenses, and tokens in their responses. Both parameter spellings and duplicate values are now masked uniformly, sensitive response headers are redacted, and SUBNET response bodies are excluded from debug dumps entirely. The leak is inherited from upstream and present in every previous release, upstreammcincluded: if you have ever shared--debugoutput of SUBNET commands (mcli license .../mcli support ...), treat the API keys and licenses in it as exposed and rotate them. - Redaction isolated from caller state: debug tracing now dumps copies of the request and response, so redaction cannot mutate objects the caller still holds. Zero-length, fixed-length, and unknown-length response bodies are all covered, and callers can still read the response normally.
Dependency Updates
Every dependency bump below except the term / mod / sync / tools refresh closes at least one published advisory in the Go vulnerability database, and govulncheck reports zero known vulnerabilities reachable from this release’s code. No security advisory has ever been published for minio/mc, minio-go, madmin-go, or minio/pkg themselves.
- Go build baseline upgraded from
1.26.2to1.26.5(the newest 1.26.x at release time), picking up the 1.26.3–1.26.5 security batches — including GO-2026-4970 (symlink-based root escape inos) and GO-2026-5856 (Encrypted Client Hello privacy leak incrypto/tls), the two most relevant to an S3 client that writes local files and speaks TLS. github.com/klauspost/compressfromv1.18.5tov1.18.7(closes GO-2026-5841).github.com/prometheus/prometheusfromv0.310.0tov0.311.3(closes GO-2026-5264, GO-2026-5381, GO-2026-5710).google.golang.org/grpcfromv1.79.3tov1.82.1(closes GO-2026-6061), with thegenprotofamily refreshed alongside.- The
golang.org/x/*family refreshed across the board:cryptov0.49.0→v0.53.0(the 14-advisory GO-2026-5005…5033 batch),netv0.52.0→v0.56.0(GO-2026-5025…5030 and GO-2026-5942),sysv0.42.0→v0.46.0(GO-2026-5024),textv0.35.0→v0.39.0(GO-2026-5970), plusterm,mod,sync, andtools. - Removed
aead.dev/minisignandgithub.com/minio/selfupdate, and synchronized the third-party credits file.
Engineering and Delivery
- Integration test dependencies pinned: CI no longer downloads the MinIO server from a mutable upstream URL. It now uses a versioned
pgsty/miniorelease archive verified by its SHA-256 digest, with Go pinned to1.26.5. - Release pipeline verification: a packaging validation workflow compares the binary inside all three package formats byte-for-byte against the build output, and checks package names, checksums, architecture fields, and every metadata field. The expected RPM metadata is sourced from the signing script itself, so configuration drift cannot strand a release part-way through signing.
- CI supply-chain hardening: every GitHub Action is pinned to a commit SHA with dependabot keeping them current, workflow permissions are narrowed to read-only, and a stale workflow pointing at the upstream organization’s project board was removed.
- Documentation: the English and Chinese READMEs now state this fork’s distribution channels and self-update policy explicitly, and installation instructions that would silently install upstream
mcwere removed.
Known issue
mcli watch (bucket event notification) receives no events against any published pgsty/minio server release. The cause is a silent streaming-flush regression on the server side, inherited from upstream — it is not a client problem, and the previous mcli release is affected identically. The fix was merged to the server’s master on 2026-07-29 but has not shipped in a published server release. See the SILO 20260618 release notes and PR #34.
Separately, this fork inherits upstream’s unfixed defects, and with the upstream repository archived they can only ever be fixed here. The most serious is minio/mc#5139: mirror --remove --watch can delete a live object from the target when a non-current version of it is removed from the source. Exercise caution combining --remove --watch on versioned buckets.
Related Commits
- 9603ee3: fix: redact SUBNET secrets in HTTP debug logs
- f6ae2b0: fix: disable self-update in Pigsty builds
- c05a6e4: build: update Go deps and toolchain to 1.26.5
- 1f105aa: build: use local fork artifacts for containers
- 1182da5: ci: pin fork integration test dependencies
- 9ee207f: docs: clarify Pigsty fork distribution channels
- 0686cd8: fix: isolate SUBNET debug redaction
- ad10a2a: build: complete local Docker context isolation
- 5f54221: docs: update mc README and cn version
- 02c0305: build: migrate release packaging to nFPM
- 4c4dcc4: build: harden release provenance and package metadata
31 - Silo 20260618 Released
Published: 2026-06-18 · Version: RELEASE.2026-06-18T00-00-00Z
This release is a security and dependency-maintenance update for the pgsty/minio fork. It hardens LDAP STS throttling, completes S3 Select oversized-record enforcement, removes the obsolete ReadMultiple internode storage-REST API, upgrades the Go build baseline to 1.26.4, and refreshes Go module dependencies to pick up additional third-party security fixes.
Note
Known issue: this release — like every earlier community release since RELEASE.2025-12-03T12-00-00Z — carries a silent streaming-flush regression inherited from upstream that breaks mc watch / bucket-notification listeners and S3 Select keep-alives. The fix (PR #34) shipped in SILO 20260804.
Major Changes
- Remove the obsolete
ReadMultiplestorage-REST API: the legacy/rmplinternode endpoint is removed rather than patched in place, including its route, handler, client wrapper, storage interfaces, xlStorage methods, generated datatypes, and related metric. No production caller is expected after upstream multipart handling moved toReadParts, but clusters should still run a consistent release during rolling upgrades. - Complete S3 Select oversized-record enforcement: JSON Lines input now uses the bounded reader path, so oversized records are rejected consistently instead of bypassing limits on SIMD-capable CPUs. S3 Select stream errors now preserve the intended error code and wrap JSON parser failures as
JSONParsingError. - Harden LDAP STS rate-limit source bucketing: throttling is now keyed only by source IP, avoiding username-shared buckets that could be drained by one client to lock out a legitimate user. Trusted-proxy handling now resolves
X-Forwarded-Forfrom right to left, rejects catch-all trusted-proxy CIDRs, ignores RFC 7239Forwarded, and documents theX-Real-IPdeployment contract. - Refresh the Go runtime and module baseline: release, hotfix, goreleaser, and old-CPU Docker builds now use
golang:1.26.4-alpine;go.modis updated to Go1.26.4; and dependencies are refreshed across NATS, Prometheus, Azure SDK, Apache Thrift, gRPC, OpenTelemetry, Google API/auth, Gox/*, and related transitive libraries.
Direct Security Fixes
- CVE-2026-42600: remove the obsolete
ReadMultiplestorage-REST API to close the legacy internode file-read path exposed through/rmpl. - CVE-2026-39414: complete oversized S3 Select record enforcement for JSON Lines inputs and preserve correct S3 Select error semantics.
- CVE-2026-33419: further harden LDAP STS rate-limit accounting and trusted-proxy source-IP handling.
Dependency Security Updates
- Update
github.com/Azure/go-ntlmsspfromv0.1.0tov0.1.1, fixing CVE-2026-32952, where malformed NTLM challenges could panic a Go process. - Update
github.com/apache/thriftfromv0.22.0tov0.23.0, fixing CVE-2026-41602 in the GoTFramedTransportimplementation. - Update
github.com/nats-io/nats-server/v2fromv2.11.1tov2.11.15, absorbing the NATS 2.11.x security patch line. Notable fixes include pre-auth WebSocket and leafnode denial-of-service issues, MQTT authorization issues, JetStream management API authorization hardening, credential exposure fixes, and request identity-spoofing fixes, including CVE-2026-27889, CVE-2026-29785, CVE-2026-33217, CVE-2026-33218, CVE-2026-33222, and CVE-2026-33247. - Update
github.com/prometheus/prometheusfromv0.310.0tov0.311.3, absorbing Prometheus security fixes for remote-read denial of service, stored XSS in UI surfaces, and remote-write configuration secret exposure, including CVE-2026-42154, CVE-2026-44903, CVE-2026-42151, and CVE-2026-40179. - Upgrade the release build baseline through Go
1.26.4and refresh supporting Go module families, includinggolang.org/x/crypto,golang.org/x/net,golang.org/x/sys,golang.org/x/text,google.golang.org/grpc, and OpenTelemetry. These updates keep the fork aligned with patched upstream dependency baselines even where the previously pinned version was already past the specific public advisory range.
Related Commits
32 - Silo 20260417 Released
Published: 2026-04-17 · Version: RELEASE.2026-04-17T00-00-00Z
This release focuses on security hardening and compatibility tightening. It bundles fixes across OIDC, LDAP STS, S3 Select, replication metadata handling, unsigned-trailer flows, the Snowball upload path, and multiple dependency- and Go toolchain-related security issues, while also incorporating the LDAP TLS regression fix and a cleanup of community-fork documentation.
Major Changes
- Tighten the identity-authentication flow: OIDC / WebIdentity now accepts only asymmetrically signed
ID Tokenvalues backed by the IdPJWKS; symmetrically signed tokens such asHS256are no longer accepted. LDAP STS also now hides the distinction between unknown-user and bad-password failures to reduce username-enumeration risk. - Update LDAP STS rate limiting: limits now apply to both source IP and normalized username, and successful requests no longer consume quota incorrectly. By default MinIO now uses only the socket peer address as the source and no longer trusts
X-Forwarded-For,X-Real-IP, orForwarded; to rate-limit by real client IP, configureMINIO_IDENTITY_LDAP_STS_TRUSTED_PROXIESexplicitly. - Make upload and write paths stricter: presigned query parameters can no longer be combined with
unsigned-trailerPUTor multipart uploads. Snowball auto-extract now also performs full signature validation on theunsigned-trailerpath and rejects anonymous or forged-signature requests. - Prevent replication metadata spoofing: internal
X-Minio-Replication-*headers attached to ordinaryPUT/COPYrequests are now rejected or ignored, and only trusted replication flows may write the related internal metadata. - Clarify S3 Select error semantics: oversized CSV and line-delimited JSON records now return
OverMaxRecordSizedirectly instead of the genericInternalError; clients or alerting rules that depend on the old error code should be adjusted. - Upgrade the runtime and dependency baseline: fix the regression where
ldaps://did not correctly apply TLS settings, replaceminio/pkg/v3withpgsty/minio-pkg/v3, and pin several critical dependencies that are prone to breaking changes. The release also upgradesgo-jose,go.opentelemetry.io, and Go1.26.2to unify the build and release baseline. - Refresh documentation and security guidance: update
SECURITY.md,VULNERABILITY_REPORT.md,docs/sts/ldap.md, and related documents, add a security advisory index, and switch upstreamminio/minioreferences in the security guidance over topgsty/minio.
Fixed CVEs
- CVE-2026-34986: upgrade
go-josetov4.1.4and fix known security issues in the JWT / JOSE dependency chain. - CVE-2026-39883: upgrade the
go.opentelemetry.iodependency stack to fix the PATH-hijacking risk. - CVE-2026-33322: restore the strict JWKS-only OIDC JWT verification path to block keyring injection and algorithm-confusion risk.
- CVE-2026-33419: systematically harden LDAP STS authentication, rate limiting, source-address identification, and accounting logic across four follow-up fixes.
- CVE-2026-34204: reject injection of
X-Minio-Replication-*metadata by untrusted requests to prevent objects from being written with invalid replication state. - CVE-2026-39414: reject oversized S3 Select records early to avoid continued buffering and parsing of abnormal inputs.
- GHSA-hv4r-mvr4-25vw: close the unsigned-trailer query-auth bypass.
- GHSA-9c4q-hq6p-c237: harden unsigned-trailer authentication and signature validation in Snowball auto-extract scenarios.
- CVE-2026-32280, CVE-2026-32281, and CVE-2026-32283: upgrade Go to
1.26.2and absorb the upstream toolchain and stdlib security fixes.
Related Commits
- ce1c537: fix: pin deps with breaking changes and fix LDAP TLS regression (#15)
- 68e0ba9: fix: upgrade go-jose to v4.1.4 to patch CVE-2026-34986
- 1869bd3: fix: CVE-2026-39883 upgrade go.opentelemetry.io
- ff58df9: merge: PR #18 upgrade go-jose to v4.1.4 for CVE-2026-34986
- e4fa063: merge: PR #19 upgrade go.opentelemetry.io for CVE-2026-39883
- d24f449: fix: CVE-2026-33322 restore JWKS-only OIDC JWT verification
- 3b950f8: fix: CVE-2026-33419 harden LDAP STS auth
- 56fa63b: fix: CVE-2026-34204 reject untrusted replication metadata
- 3252d5b: fix: CVE-2026-39414 reject oversized S3 Select records
- f444b6f37: fix: fake CVE-2026-40027 block unsigned-trailer query auth bypass
- efb6e5b00: fix: fake CVE-2026-40028 harden snowball unsigned-trailer auth
- db4c0fd: fix: CVE-2026-32280/CVE-2026-32281/CVE-2026-32283 upgrade Go to 1.26.2
- 18b712d: fix: CVE-2026-33419 preserve LDAP STS rate limits on success
- 9e10f6d: fix: CVE-2026-33419 harden LDAP STS rate-limit source IP
- f441108: fix: CVE-2026-33419 tighten LDAP STS rate-limit accounting
- f48dbe7: docs: refresh security guidance and fork references
33 - Silo 20260325 Released
Published: 2026-03-25 · Version: RELEASE.2026-03-25T00-00-00Z
This maintenance release bundles mcli/mc into the Docker image, fixes an LDAP TLS configuration regression on ldaps:// connections, and pins security-patched versions of key dependencies.
Major Changes
- Bundle
mcli(and itsmccompatibility alias) into the Docker image with checksum verification so administrative CLI tools are available out of the box. - Fix the LDAP TLS regression affecting
ldaps://deployments so custom root CAs andMINIO_IDENTITY_LDAP_TLS_SKIP_VERIFYare properly honored. - Remove inherited upstream CI/CD workflows that are no longer used in the community fork.
- Pin key dependencies to isolate upstream breaking changes and incorporate the security fixes below.
Fixed CVEs
- CVE-2026-24051: Pin
go.opentelemetry.io/otel/sdk v1.42.0to address the macOSPATH-hijacking arbitrary code execution vulnerability. - CVE-2025-10543: Upgrade to
github.com/eclipse/paho.mqtt.golang v1.5.1, fixing incorrect MQTT packet encoding for oversized UTF-8 strings. - CVE-2025-58181: Upgrade to
golang.org/x/crypto v0.49.0, fixing unbounded memory consumption insshGSSAPI authentication handling.
Related Commits
34 - Silo 20260321 Released
Published: 2026-03-21 · Version: RELEASE.2026-03-21T00-00-00Z
This maintenance release is built around the Go 1.26.1 upgrade and a broad dependency refresh. Beyond stricter compiler and linter compatibility fixes, it also delivers the most substantial security dependency refresh in the current release line.
Major Changes
- Upgrade the build environment from Go
1.26.0to Go1.26.1. - Refresh direct and indirect dependencies to converge on the newer toolchain.
- Fix linter and test issues exposed by the stricter Go 1.26.1 checks.
Fixed CVEs
- CVE-2026-27137: Go stdlib
1.26.0->1.26.1fixes incomplete email-constraint enforcement incrypto/x509. - CVE-2026-27138: Go stdlib
1.26.0->1.26.1fixes acrypto/x509panic triggered by malformed certificates. - CVE-2026-25679: Go stdlib
1.26.0->1.26.1fixes insufficient validation of IPv6 host literals innet/url. - CVE-2026-27139: Go stdlib
1.26.0->1.26.1fixesFileInfometadata escaping theRootboundary inos. - CVE-2026-27142: Go stdlib
1.26.0->1.26.1fixes missing URL escaping inhtml/templateformeta refreshcontent. - CVE-2026-26958:
filippo.io/edwards25519v1.1.0->v1.2.0fixes incorrect or undefinedMultiScalarMultbehavior. - CVE-2025-10543:
github.com/eclipse/paho.mqtt.golangv1.5.0->v1.5.1fixes incorrect MQTT packet encoding for oversized UTF-8 strings. - CVE-2026-24051:
go.opentelemetry.io/otel/sdkv1.38.0->v1.42.0fixes the macOS PATH-hijacking arbitrary code execution issue. - CVE-2026-33186:
google.golang.org/grpcv1.77.0->v1.79.3fixes authorization bypass caused by a missing leading slash in the HTTP/2:pathpseudo-header.
Related Commits
35 - Silo 20260314 Released
Published: 2026-03-14 · Version: RELEASE.2026-03-14T12-00-00Z
This release switches the project to the community-maintained Console fork and performs a sizeable dependency refresh to establish the base for the later Go 1.26.x maintenance releases.
Major Changes
- Switch to the community-maintained
georgmangold/console v1.9.1fork in place of the unmaintainable upstream Console dependency. - Refresh a large portion of the direct and indirect dependency graph so the new Console and toolchain combination builds cleanly.
- Fix the
go vetformat directive issue ingrid_test.goand adjust tests for the HTTP behavior changes in Go 1.26.
Fixed CVEs
- CVE-2025-47913:
golang.org/x/cryptov0.37.0->v0.46.0fixes a panic inssh/agentwhen handling malformed responses. - CVE-2025-58181:
golang.org/x/cryptov0.37.0->v0.46.0fixes unbounded memory consumption insshGSSAPI authentication parsing. - CVE-2025-47914:
golang.org/x/cryptov0.37.0->v0.46.0fixes a panic inssh/agentcaused by malformed identity messages. - CVE-2025-47911:
golang.org/x/netv0.39.0->v0.48.0fixes quadratic parsing complexity inhtml.Parsefor crafted inputs. - CVE-2025-58190:
golang.org/x/netv0.39.0->v0.48.0fixes an infinite parsing loop ingolang.org/x/net/html.
Related Commits
36 - Silo 20260214 Released
Published: 2026-02-14 · Version: RELEASE.2026-02-14T12-00-00Z
This early infrastructure-focused community release restores the embedded Console, introduces GitHub CI/CD, and lifts the Go baseline to 1.26.0, which also absorbs a batch of security fixes from the older toolchain generation.
Major Changes
- Restore the embedded Console and refresh the README to clarify the community fork position.
- Add GitHub CI/CD workflows as the base for automated builds and multi-platform delivery.
- Add quick links for docs, Docker, the GitHub repository, and installation through the
pigpackage manager.
Fixed CVEs
These issues were absorbed as part of the Go 1.25.5 -> 1.26.0 upgrade:
- CVE-2025-68121:
crypto/tlscould incorrectly accept mutated CA configuration during session resumption. - CVE-2025-61730: TLS 1.3 could process handshake messages incorrectly across encryption-level boundaries.
- CVE-2025-61726:
net/urlquery parsing could be abused for memory exhaustion. - CVE-2025-61728:
archive/zipcould consume excessive CPU while building archive indexes. - CVE-2025-68119:
cmd/gocould trigger unexpected code execution when invoking external VCS tooling. - CVE-2025-61731: the
#cgo pkg-config:directive could be abused for arbitrary file writes. - CVE-2025-61732:
cmd/cgocomment parsing discrepancies could enable code smuggling.
Related Commits
- 8630937: Restore embedded console and update README for community fork
- 68521b3: add github ci/cd pipeline
Historical note: CI/CD pipeline evidence links maintained-history commit 68521b37f, which preserves the pipeline files and includes a README adjustment.
37 - Silo 20251203 Released
Published: 2025-12-15 · Version: RELEASE.2025-12-03T12-00-00Z
This is the earliest traceable community release, establishing the community packaging and distribution baseline.
Major Changes
- Build the community packaging flow around
minio/pkger. - Choose a maintenance-mode upstream MinIO baseline as the starting point for the community-maintained fork.
- Produce the first
apk,deb, andrpmartifacts for ongoing community releases.
Fixed CVEs
- Initial community release baseline; no delta CVE list against an earlier community version applies.
Related Commits
- d4cd4b4: RELEASE.2025-12-03T12-00-00Z with go 1.25.5