Silo 20260903 Released
Released: 2026-09-03 · Version: RELEASE.2026-09-03T00-00-00Z · Previous release: RELEASE.2026-08-06T00-00-00Z
SILO 20260903 is the security and correctness release following the first fully rebranded Silo release. Its main theme is not a new delivery surface but a tighter set of 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 deliberately uses 20260806 as its only release baseline. Intermediate component builds remain identifiable in their source repositories, but the public documentation below consolidates the complete server, Console, shared-package, and client result delivered on 2026-09-03.
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. The standalone Console release and the server embedding remain separately verifiable artifacts.
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 was implemented end to end rather than as a handler-only shim:
- 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, defaulting the server topgsty/silo:RELEASE.2026-09-03T00-00-00Zand post-install jobs topgsty/mc:RELEASE.2026-09-03T07-13-05Z
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 adversarial tests because each protects a reproduced failure rather than a hypothetical abstraction.
The complete adversarial review, including the fixes found after the first “ready” claim, is recorded 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. This does not make a mixed-version cluster supported.
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. A reviewed single-object repair exists outside this release, but batch semantics and the original PR are incomplete; shipping a partial condition contract would be riskier than the documented deferral. - 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.
ListMultipartUploadsfilters (#79).prefixbehaves like an exact key match, whilemax-uploads,key-marker, anddelimiterare not fully honored; see the conformance analysis.- 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. - 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 production-safety boundary.
Verification and evidence boundary
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 the reviewed server line, git diff --check, the CORS/Object Lock targeted race tests, rebrand guard, generated-file check, lint, Helm lint/render/package, and the seven-resource legacy-upgrade identity guard all pass; remote Go CI and VulnCheck are green on the final pre-release main line.
Delivery remains independently checkable rather than inferred from those source tests. The server GitHub release identifies the exact tagged source and package assets. 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 release’s final shape also reflects adversarial review: findings were reproduced before repair, inherited limitations were separated from release regressions, and validation claims were rerun against the tree they describe.
The complete authorship record remains in CONTRIBUTORS.md.