S3 Explorer — manage S3-compatible object storage from your desktop
Cross-platform Tauri + Vue 3 client supporting AWS S3, MinIO, Backblaze B2, Cloudflare R2, Dell ECS, and 17+ other S3-compatible providers.
What's new in v0.3.8
Compatibility release. v0.3.7 was skipped — its work is folded into this version. Three independent improvements ship together: real S3-compat support, a much better network diagnostic, and a more usable browsing UI on long paths.
🪣 Real S3-compat — fix DeleteObjects on Dell ECS, MinIO, Cloudflare R2, Wasabi, Backblaze B2, Tigris…
A user on Dell ECS reported Operation failed: invalid request when deleting a folder with two files. The root cause turned out to be far broader than one provider:
The AWS SDK Rust 1.69+ flipped RequestChecksumCalculation from WhenRequired to WhenSupported, injecting a x-amz-sdk-checksum-algorithm: CRC32 header + x-amz-checksum-crc32 trailer over aws-chunked body encoding on every supporting operation. AWS S3 itself accepts both modes, but most S3-compat servers don't recognise the modern flexible-checksum framework and reject the request — Dell ECS as InvalidRequest, older MinIO as Missing required header: Content-MD5, Cloudflare R2 as NotImplemented: Header x-amz-checksum-crc32, and so on.
v0.3.8 ships a defense-in-depth fix:
- SDK upgrade —
aws-sdk-s3 1.17 → 1.132,aws-config 1.1 → 1.8(5 minors of upstream fixes, plus the future-proofingChecksumAlgorithm::Md5enum landing). RequestChecksumCalculation::WhenRequired— disables auto CRC32 injection on every operation that doesn't strictly need it (PutObject, ListObjectsV2, GetObject…). Restores the pre-2025 SDK behaviour that S3-compat servers know how to parse.- New
LegacyChecksumStripperinterceptor — for the few operations likeDeleteObjectswhose Smithy trait declareshttpChecksumRequired: trueand ignoreWhenRequired, this Smithy interceptor stripsx-amz-checksum-*,x-amz-trailer, andx-amz-sdk-checksum-algorithmheaders before transmit. Last line of defense. map_s3_errorenriched — when an S3-compat server rejects a request, the user now sees the server's actual message (e.g.InvalidRequest: <Dell ECS message exact>) instead of our generic explanation.extract_server_messageparses the<Message>field from the SDK's debug repr.
🔍 Network diagnostic — multi-step pipeline
The connectivity test in Settings → Advanced → Network was a single pass-or-fail probe — useful but opaque. v0.3.8 turns it into a full diagnostic pipeline:
| Step | What it checks | What it tells you |
|---|---|---|
| DNS resolution | Resolves api.lemonsqueezy.com via system + DoH cascade (Cloudflare 1.1.1.1 + Google /resolve) |
All IPs returned, suspicious responses (loopback / RFC1918), latency |
| TCP connect | Opens a socket to each resolved IP | Per-IP connect time, happy-eyeballs IPv4↔IPv6 fallback if one family is broken |
| TLS handshake | Negotiates TLS through your configured proxy + OS trust store | Certificate chain length, verified subject, cipher suite |
| API contract | HEAD /v1/licenses/activate |
Confirms the endpoint actually responds (catches transparent gateway errors) |
| Sanity | Response analysis | Detects body redirection / captive portals |
Each step shows its outcome (ok / warning / error / skipped), a human message, structured details, and elapsed milliseconds — so when the License tab fails, you can see exactly which layer broke.
Other related fixes:
- DNS multi-IP —
resolve_with_fallbacknow returnsVec<IpAddr>instead ofOption<IpAddr>. Some hosts (especially in corporate networks behind /etc/hosts overrides or split-horizon DNS) have multiple A/AAAA records, and we previously dropped all but the first — breaking happy-eyeballs IPv4↔IPv6 fallback. - Coherent HTTP client —
build_http_client_for_hostfactored so license activation and the diagnostic share the same TLS/proxy/DNS configuration. No more "diagnostic says OK, activation fails".
🧭 Adaptive breadcrumb
The breadcrumb in ObjectBrowser had two failure modes on long paths: it overflowed off the right edge (cutting the current folder, the most important segment), and it hid intermediate segments under ... without a way to read them.
v0.3.8 reworks it as an adaptive component:
- 5-step truncation strategy based on the actual toolbar width (measured via
ResizeObserver):- Show all segments full
- Collapse middle:
[first, "...", last] - Truncate first:
[first_t, "...", last] - Drop first entirely:
["...", last] - Last resort:
["...", last_truncated_with_ellipsis]
- The current folder is always visible — truncated mid-name (
hhh...hhh) only when the window is genuinely too narrow. truncateMiddle(text, max)+truncateEnd(text, max)helpers informatters.ts.- The pill is content-sized — when the path is short, the freed space goes to the search bar (which had a needless
max-w-mdcap, now removed). - Click-to-edit mode keeps the pill's exact width so the box doesn't shrink/grow when toggling between view and edit.
📂 Indexation behaviour — explicit "no auto-indexing"
Settings → Performance → max indexing requests had a 1-100 range, with no way to disable indexing for very large buckets where the user just wants to navigate. v0.3.8 expands this to 0-5000:
- 0 = no indexing on bucket open at all (counts/sizes display as
—, manualFull Indexbutton still works) - 1-5000 = caps the auto-indexing at that many ListObjectsV2 calls
- The
Full Indexbutton uses an internalu32::MAXsentinel to bypass any cap
🪟 UI compaction
- BucketList — bucket creation date moved from the card body into a tooltip on the bucket name. Card padding tightened from
p-3topx-3 py-2. Saves ~16-18 px per card → roughly one extra bucket visible on screen. - Sidebar footer — Metrics & Settings buttons row padding
p-4→p-2. Footer height 68 px → 52 px (-16 px = ~1 bucket row reclaimed for the list).
🐛 Licensed users no longer see the purchase popup
The NagPopup (5 min timer + click-based trigger that nudges unregistered users to buy a license) was reading the frontend license store at trigger time. The store was only hydrated when the user opened Settings → License — at boot it stayed at 's0' (unregistered), so the popup fired even on machines with a valid licence active in profiles.json.
App.vue now hydrates the store via licenseStore.checkStatus() on mount, before any popup timer can fire. The backend remains the source of truth (license stays validated against Lemon Squeezy, machine-bound, tampering-detected) — we just stopped reading a stale frontend cache.
Internal
- AWS SDK upgrade brings 5 minors of upstream fixes for free (retry logic, IMDS handling, DNS caching).
- New file
s3_compat.rs— small, self-contained Smithy interceptor with unit tests. s3_errors.rsgainedextract_server_message+ 4 unit tests covering the parsed/empty/long/missing cases.- 74 cargo tests passing.
Downloads
| Platform | Download |
|---|---|
| macOS (Universal — Intel + Apple Silicon) | .dmg |
| Windows x64 | .exe · .msi |
| Windows ARM64 (Snapdragon, Surface Pro X) | .exe · .msi |
| Linux x64 | .AppImage · .deb · .rpm |
| Linux ARM64 | .AppImage · .deb · .rpm |
Existing v0.3.6 users on Linux/Windows will receive v0.3.8 automatically via the in-app updater. macOS users will receive it once the macOS bundle is uploaded.