Object storage you can audit line by line — now with a storage core that pulls zero C, and a build that needs zero protoc.
Today we released rs3gw 0.2.2 — a substantial patch that carves rs3gw into composable Cargo features, lands a genuinely C/FFI-free storage-only profile, swaps the system protoc build step for a pure-Rust compiler, and gives every bucket request/byte/cost visibility through a new /api/usage endpoint.
No C. No C++. No Go. No Fortran. rs3gw is a drop-in S3-compatible gateway — 100+ operations, the same SDKs and CLIs you already use — but where MinIO is Go and Ceph RGW is C++, rs3gw is Rust the whole way down. With 0.2.2 that claim gets sharper: the storage-only default dependency tree is now genuinely C/FFI-free — no ring, no aws-lc-sys, no native-tls, no zstd-sys, no rustls 0.23 — and the build itself no longer shells out to a system protoc, because a pure-Rust .proto compiler (protox) does that work in-process. One static binary. Sovereign. Auditable end to end.
Why 0.2.2 matters
- Modular feature flags. The HTTP/gRPC server stack is now behind a
serverfeature (default-on). Library consumers who only want the storage layer can depend on rs3gw withdefault-features = false, features = ["local"]— and that tree dropsring,aws-lc-sys,native-tls,zstd-sys, andrustls 0.23, leaving a true Pure Rust closure per COOLJAPAN policy. Cloud backends (s3,gcs,azure) and columnar formats (formats) are all opt-in now, with anall-backendsmeta-feature to flip them on together. - protoc-free builds.
build.rsnow callsprotox::compile(pure-Rustprotox0.9) to produce aFileDescriptorSetfortonic-prost-build. No systemprotocinstallation required — locally or in CI. - Per-bucket usage & cost visibility. A new
UsageTrackerfeeds/api/usageand/api/usage/{bucket}, reportingstorage_bytes,object_count, bytes uploaded/downloaded,requests_by_op, andestimated_cost.total_usd— with?flush=trueto fire registered cost hooks. - Correctness.
HEADfor SSE-encrypted objects now returns the plaintextContent-Length, consistent withGET, instead of the on-disk ciphertext length. - Modernized deps & tests. scirs2-core / scirs2-io move to 0.5.0, OxiArc compression to 0.3.3, and new fuzz / soak / observability test suites land.
Technical Deep Dive
The feature architecture. rs3gw is now sliced along clean seams. default = ["server"] keeps the familiar full server (axum, axum-server, tower, tonic, async-graphql, utoipa, the Prometheus exporter, OpenTelemetry/OTLP, reqwest webhooks). local = [] is the storage-only profile. Cloud backends are feature-gated: s3 (aws-sdk-s3 1.132 + aws-config), gcs (google-cloud-), azure (azure_); columnar/serialization formats live behind formats (parquet, arrow, apache-avro, orc-rust, prost-reflect, rmp-serde); and all-backends activates all four at once. The binaries carry required-features so they never break the storage-only build: rs3ctl requires ["server"], s3-migrate requires ["s3","server"], and testdata-generator requires ["formats"]. Across the test suite, server-only integration files now carry #![cfg(feature = "server")], format-dependent modules carry #[cfg(feature = "formats")], and the select_object_content handler and its XML select parser are gated behind formats too — so when a feature is off, those modules compile out cleanly rather than dragging in dependencies. The payoff is the Pure-Rust default closure: with default-features = false, features = ["local"], the C/FFI surface is gone.
protox in build.rs. Generating Rust from .proto used to mean a protoc binary on the PATH. 0.2.2 replaces that with protox 0.9, a pure-Rust compiler: build.rs calls protox::compile(...) to emit a FileDescriptorSet, which tonic-prost-build consumes to produce the gRPC bindings. The build is now hermetic and toolchain-free beyond Cargo and rustc (1.85+).
UsageTracker and /api/usage. src/observability/usage.rs introduces an in-memory, per-bucket accumulator. It tracks PUT/GET/DELETE request counts, bytes uploaded and downloaded, and live storage size and object count. That data backs the new REST endpoints under src/api: /api/usage returns a report with per-bucket storage_bytes, object_count, bytes_uploaded, bytes_downloaded, requests_by_op, total_requests, and estimated_cost.total_usd, plus report-level total_objects, total_storage_bytes, and generated_at. Pass ?flush=true and it invokes any registered cost-hook callbacks — the seam where you wire in real billing.
The SSE HEAD Content-Length fix. Previously, HEAD on an SSE-encrypted object reported the ciphertext length sitting on disk, while GET streamed the (shorter) plaintext — an inconsistency that tripped up clients. In src/storage/encryption.rs, HEAD now loads the encryption sidecar and derives the plaintext size: for chunked/multipart objects it sums the per-chunk plaintext_len fields; for single-PUT objects it computes single_shot_plaintext_len(ciphertext_len). If the sidecar is unavailable, it falls back, best-effort, to the on-disk size. HEAD and GET now agree.
Compression throughout src/storage runs on OxiArc — oxiarc-zstd, oxiarc-lz4, and oxiarc-deflate 0.3.3 — pure-Rust codecs, no zstd-sys in the default tree.
Getting Started
Build the server. Default features include server, and there is no system protoc to install — protox compiles the .proto definitions in pure Rust:
git clone https://github.com/cool-japan/rs3gw.git
cd rs3gw
git checkout v0.2.2
cargo build --release # no system protoc needed — protox compiles .proto in pure Rust
./target/release/rs3gw
Library consumers who want only the storage layer get the new C/FFI-free default tree:
# Cargo.toml
rs3gw = { version = "0.2.2", default-features = false, features = ["local"] }
Make a real S3 call and read the new usage endpoint (default port :9000):
aws --endpoint-url http://localhost:9000 s3 cp report.parquet s3://analytics/
curl http://localhost:9000/api/usage # per-bucket requests, bytes, estimated cost
curl http://localhost:9000/api/usage/analytics # one bucket
What’s New in 0.2.2
Modularity & Pure Rust
- New
serverfeature (default-on) gates the entire HTTP/gRPC stack; newlocalfeature is the storage-only profile. - Cloud backends feature-gated:
s3,gcs,azure, and columnarformats— all off by default, with anall-backendsmeta-feature to enable them together. - Storage-only default tree is now C/FFI-free: no
ring,aws-lc-sys,native-tls,zstd-sys, orrustls 0.23. - protoc-free builds:
build.rsuses pure-Rustprotox0.9 (protox::compile→FileDescriptorSetfortonic-prost-build). opentelemetry-otlpswitched todefault-features = false(grpc-tonic transport only), dropping the reqwest/native-tls HTTP path.tonicnow built withfeatures = ["tls-ring"]for TLS.
Observability
UsageTracker(src/observability/usage.rs): in-memory per-bucket request/byte/storage accumulator./api/usageand/api/usage/{bucket}endpoints: storage bytes, object count, bytes up/down, requests by op, andestimated_cost.total_usd.?flush=trueinvokes registered cost-hook callbacks.
Testing
xml_parser_fuzz.rs: a deterministic fuzz harness (3 tests) for the hand-rolled XML request parsers, using a seeded xorshift64* PRNG so any failure reproduces identically.soak_tests.rsandobservability_tests.rs: soak and latency-exemplar suites per the v0.3/v0.5 roadmap.usage_tests.rs: 2 integration tests covering the full/api/usagereport and the?flush=truehook path against real S3 operations.
Fixes
- SSE
HEADnow returns plaintextContent-Length, consistent withGET.
Deps
- scirs2-core / scirs2-io 0.4.4 → 0.5.0.
- oxiarc-zstd / oxiarc-lz4 / oxiarc-deflate 0.2.8 → 0.3.3.
Tips
-
Want rs3gw as a storage library, not a server? Depend on it with
default-features = false, features = ["local"]for a C-free dependency tree.rs3gw = { version = "0.2.2", default-features = false, features = ["local"] } -
Turn on cloud only when you need it. Enable
s3,gcs,azure, orformatsindividually — orall-backendsfor the lot. They are off by default to keep builds lean and the default tree pure. -
Drop
protocfrom CI. rs3gw no longer needs a systemprotoc;protoxhandles.protoat build time, so you can delete that install step from your pipeline. -
Watch your buckets.
GET /api/usage(or/api/usage/{bucket}) returns request, byte, and estimated-cost metrics; add?flush=trueto fire your registered cost hooks for billing integration. -
Remember the
formatsgate. S3 Select /select_object_contentand thetestdata-generatorbinary now require theformatsfeature — enable it if you depend on those. -
SSE
HEADnow matchesGET. If any client tests hard-coded the ciphertext size, update them to expect the plaintextContent-Length.
This is the foundation
rs3gw sits squarely inside the COOLJAPAN ecosystem. It’s built on scirs2-io and the broader SciRS2 stack (now 0.5.0), compresses through OxiArc 0.3.3, and builds with pure-Rust protox — no system toolchain beyond Cargo and rustc. It lives alongside SciRS2, NumRS2, OptiRS, PandRS, OxiBLAS, OxiCode, and OxiZ: a coherent, sovereign, all-Rust foundation where each layer is auditable and replaceable.
Repository: https://github.com/cool-japan/rs3gw
Object storage shouldn’t require you to trust a binary you can’t read. With 0.2.2, the storage core is pure Rust, the build is hermetic, and every byte and dollar is something you can query.
Star the repo if you believe infrastructure should be sovereign, auditable, and C-free.
— KitaSan at COOLJAPAN OÜ June 17, 2026