A defect sweep across a workspace is only as good as how many of its findings actually get fixed.
Today we released OxiGeo 0.2.1 — a production-hardening release built around a workspace-wide, multi-agent defect sweep across every crate. It surfaced 342 confirmed defects and turned 314 of them into real fixes across 38 crate lanes (~520 files). The remaining 79 weren’t swept under the rug either — each is left with a typed error (Unsupported* / NotImplemented / DecodingError) instead of silently wrong output.
No C. No C++. No Fortran. And as of 0.2.1, one dependency less than that promise used to cover: oxigeo-kafka — the workspace’s only crate that ever required a C toolchain — is retired for good, so cargo check --workspace --all-features no longer needs cmake or a system C compiler anywhere in the tree. OxiGeo still compiles to a single static binary (or WASM) and runs everywhere Rust runs.
Why 0.2.1 is a game changer
A “we found N bugs” release note is easy to write and easy to under-deliver on. Half-fixed findings, silently dropped edge cases, and defect counts with no fix count attached are common enough that the number alone doesn’t mean much:
- Format decoders that silently return wrong data instead of failing are worse than a crash — the caller gets no signal anything went wrong
- A “supports GeoTIFF” claim that quietly drops out-of-line georeferencing tags fails exactly the production files that need it most
- A
WHERE-clause filter that fails open on a parse error isn’t a bug, it’s a mass-delete waiting to happen - A “no_std / embedded” claim nobody has actually cross-compiled for real hardware is a claim, not a fact
OxiGeo 0.2.1 closes all of that out:
- 342 confirmed defects, 314 fixed (47 critical / 84 high / 83 medium / 33 low) across 38 crate lanes — the other 79 are documented, typed-error deferrals, not silent gaps
- Two CRITICAL silent-corruption bugs fixed: JPEG2000 multi-tile decode (every tile beyond the first silently returned tile 0’s pixels) and GRIB2 DRT 5.40 (a JPEG2000/PNG/CCSDS payload could fall through to the plain bit-unpacker and decode as garbage)
Dataset::clip()actually clips now — every raster read after a clip (read_band,bands,statistics,convert,read_window) crops to the clip window instead of silently reprocessing the full source raster- WFS-T fails closed — an unparseable CQL filter used to match every feature (mass delete/update); it now rejects the request
no_stdis real and verified foroxigeo-core/oxigeo-embedded— actual--target thumbv7em-none-eabihfand--target riscv32imac-unknown-none-elfbuilds, not just a#![no_std]attribute that happened to compile on the host- GitHub issue #12 fixed: GeoTIFF metadata (CRS, geotransform, bounds) silently came back
Nonefor striped TIFFs where the georeferencing tags land past the first 8 KiB — the peek parser now looks up to 1 MiB
Technical Deep Dive: what the sweep actually touched
-
Format drivers. GeoTIFF gets real planar-configuration (
PlanarConfiguration=2) decoding, authoritative EPSG projected/geographic classification, and a working JPEG/WebP writer path. The Shapefile polygon reader now reconstructs multi-part polygons by ESRI ring winding (clockwise exterior / CCW hole) with containment-based hole assignment instead of merging rings from separate islands into one polygon. NetCDF-4 and HDF5 readers recurse into HDF5 sub-groups instead of silently dropping their variables, and the HDF5 writer’s chunking/compression/fill-value hints now go through a real chunked write path instead of being accepted and ignored. -
A new gateway serving layer.
oxigeo-gateway’sGateway::serve()used to accept TCP connections and do nothing with them. 0.2.1 replaces it with a real axum 0.8 HTTP service:GatewayServer/GatewayServerBuilderwire upGET /health,GET /gateway/metrics,POST /graphql(plus GraphiQL and a/graphql/wssubscription endpoint), aGET /wsWebSocket upgrade, and a load-balanced reverse-proxy fallback with real hyper-based streaming, HTTPS upstreams over Pure-Rust OxiTLS, and circuit-breaker-aware retries. The crate’s own test suite grew from 266 to 381 tests. -
Security and memory-safety hardening. Beyond the WFS-T fail-closed fix, header-driven allocation caps now bound NetCDF, HDF5, GRIB, and GeoTIFF parsing so a crafted header can’t trigger a multi-gigabyte allocation, and gateway load-balancer health checks issue genuine HTTP/1.1-over-TCP requests instead of always reporting backends healthy.
-
Dependency and supply-chain hygiene.
oxigeo-kafka(rdkafka-sys→cmake→ librdkafka, 4,831 lines with zero in-workspace reverse dependents) is retired and its crates.io versions yanked;oxigeo-proj’s vestigialproj-sysC-bindings feature goes with it. A newdeny.tomlenforces the advisory/bans/license policy,cargo-macheteremoved genuinely-unused dependencies from 66 crates, andNOTICE/THIRD_PARTY.mdship for Apache-2.0 §4(d) attribution.
Getting Started
[dependencies]
oxigeo = "0.2" # GeoTIFF + GeoJSON + Shapefile by default
use oxigeo::Dataset;
fn main() -> oxigeo::Result<()> {
let dataset = Dataset::open("world.tif")?;
println!("Format : {}", dataset.format());
println!("Size : {}x{}", dataset.width(), dataset.height());
println!("CRS : {}", dataset.crs().unwrap_or("unknown"));
Ok(())
}
The new gateway serving layer is its own crate:
use oxigeo_gateway::{GatewayConfig, GatewayServer};
use oxigeo_gateway::loadbalancer::Backend;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let server = GatewayServer::builder(GatewayConfig::default())
.with_backend(Backend::new("api".into(), "http://127.0.0.1:9000".into(), 1))
.build()?;
// Binds and serves until ctrl-c / SIGTERM (graceful shutdown).
server.serve("0.0.0.0:8080").await?;
Ok(())
}
What’s New in 0.2.1
- 342 confirmed defects found, 314 fixed across 38 crate lanes (~520 files); 79 honestly deferred with typed errors, categorized in
TODO.md - Two CRITICAL silent-corruption fixes: JPEG2000 multi-tile decode, GRIB2 DRT 5.40 dispatch
Dataset::clip()fixed to actually bound subsequent reads- WFS-T CQL filtering fails closed on unparseable filters instead of matching everything
- New axum-backed
oxigeo-gatewayserving layer: GraphQL + WebSocket + load-balanced reverse proxy, with real caching/compression/auth middleware oxigeo-kafkaretired, taking the workspace’s last mandatory C-toolchain dependency with it;oxigeo-proj’sproj-sysfeature dropped for the same reasonno_stdverified for real onoxigeo-core/oxigeo-embeddedvia actual Cortex-M4 and RISC-V cross-compiles- HA module rebuilt: PITR/snapshot/backup/DR were fabricated (canned bytes, always-pass tests) — now real WAL-backed persistence plus a genuine Raft log-replication module
- ML pipeline fixed: pruning/quantization no longer corrupts ONNX files (real protobuf tensor transforms);
oxigeo-ml-foundationgets a genuine trainable scirs2-neural backend - Header-driven allocation-DoS hardening across NetCDF/HDF5/GRIB/GeoTIFF;
deny.tomlplus acargo-machetedependency cleanup across 66 crates - Quality gates:
cargo fmtclean, clippy 0 warnings (--all-features --all-targets), 17,723 tests passed / 0 failed / 100 skipped, 416 doc tests,cargo deny checkpassing
Tips
- Migrating off Kafka?
oxigeo-kafkais gone for good — no future releases, versions yanked from crates.io. Useoxigeo-streaming,oxigeo-kinesis,oxigeo-pubsub, oroxigeo-mqttfor the sibling messaging crates that remain supported, or pullrdkafkadirectly into your own code if you specifically need Kafka. Workflow definitions can still describe a Kafka endpoint —IntegrationType::Kafka/MessageQueueType::Kafkainoxigeo-workfloware pure-Rust metadata enums, untouched by the retirement. - Re-check any
Dataset::clip()+ full-raster-read combination. If your code calledclip()and relied on something downstream implicitly picking up the clipped bounds, that was silently reading the whole source raster before 0.2.1 — worth a quick audit ifclip()is in your hot path. - Building
--all-features? Drop yourcmake/ C-toolchain setup step. Withoxigeo-kafkaandoxigeo-proj’sproj-sysgone,cargo check --workspace --all-featuresno longer touches a C compiler — CI images and dev-container setup can lose that dependency entirely. - Try the gateway with
GatewayServer::builder(config).build()?.router()for in-process testing — it returns a plainaxum::Routerwith no socket bind, useful for integration tests that exercise the GraphQL/WebSocket/proxy routes without opening a port. - WFS-T users: unparseable CQL now means a rejected request, not a silent match-all. If any client code was (even accidentally) relying on a malformed filter matching every feature, that path now returns an error — treat it as the security fix it is, not a regression to route around.
no_stdtargets are now genuinely tested, not just declared. If you’re cross-compilingoxigeo-coreoroxigeo-embeddedfor Cortex-M or RISC-V, 0.2.1 is the first release where those targets were actually built and verified as part of the release process.
This is the foundation
OxiGeo 0.2.1 leans on the same Pure Rust COOLJAPAN stack as every release before it: CRS transforms via OxiProj, HDF5/NetCDF read-write through oxih5 (now pinned to 0.2.2, with a scalar-attribute padding bug fixed upstream) and oxinetcdf, SQLite via oxisql-sqlite-compat (Limbo), TLS via OxiTLS, compression across the format drivers via the OxiArc family, ML tensor math via SciRS2-Core (bumped to 0.6.4 this cycle), and model export validated against OxiONNX. Every one of those is itself Pure Rust — which is how a 75-crate, ~784K-SLoC workspace ships a defect-hardening release without picking up a single new native dependency.
Repository: https://github.com/cool-japan/oxigeo
Star the repo if you’d rather your geospatial dependency tell you when a decode went wrong than hand you silently corrupted pixels. 342 findings, 314 fixes, and the last C dependency gone — that’s what a hardening release is supposed to look like.
The era of “it probably decoded right” is over. Pure Rust geospatial is here — fast, safe, and sovereign.
— KitaSan at COOLJAPAN OÜ July 28, 2026