OxiGeo 0.2.1 Release
[0.2.1] - 2026-07-28
Production-hardening campaign (2026-07): a workspace-wide, multi-agent defect
sweep across all 76 crates surfaced 342 confirmed defects
(47 critical / 84 high / 83 medium / 33 low). 314 were fixed across 38 crate
lanes (~520 files changed); the remaining 79 were honestly deferred, each left
with a safe typed-error path — a loud Unsupported* / NotImplemented /
DecodingError rather than silent or fabricated data. Quality gates all green:
cargo fmt --check clean; cargo clippy --workspace --all-features --all-targets
0 warnings; cargo nextest run --all-features 17,723 passed / 0 failed /
100 skipped (16,307 passed / 0 failed / 79 skipped on default features); 416
doc tests passing; cargo deny check passing. The categorized list of
deferrals carried to v0.3.0 is in TODO.md.
Fixed
Format drivers
- oxigeo-jpeg2000: two CRITICAL correctness bugs fixed — multi-tile decode now
Psot-bounds each tile's bitstream and composites it at its real pixel offset
(previously every tile silently returned tile 0), and the JP2 box parser now
recurses intojp2hsoihdr/colrin spec-conformant.jp2files are read - oxigeo-geotiff: real planar-configuration (
PlanarConfiguration=2) decoding;
authoritative EPSG projected/geographic classification; a working JPEG/WebP writer
path; the silentGeoKeyDirectoryerror and a policy-violatingexpect()removed;
ausize-overflow bug in header-driven allocation fixed - oxigeo (umbrella): fixed GitHub issue #12, "Metadata missing when reading
geotif" — the lightweightextract_tiff_info()peek parser used byDataset::open()
(distinct from the fulloxigeo-geotiffdriver above) only scanned a GeoTIFF's
first 8 KiB, soModelPixelScaleTag/ModelTiepointTag/GeoKeyDirectoryTagvalues
stored out-of-line past that offset — routine for striped TIFFs with many strips —
were silently treated as absent andcrs()/geotransform()/bounds()all returned
Noneeven though the tags were present and well-formed; the peek buffer now
extends up to a bounded 1 MiB when a georeferencing tag's value lands past the
initial window, a Y-axis sign inversion in the derivedGeoTransformis fixed
(ModelPixelScaleTag's Y scale is a positive magnitude per spec but
GeoTransform::north_upexpects a negativepixel_height), andbounds()—
previously hardcoded toNone— is now derived from the geotransform and raster
dimensions; regression testtest_issue_12_far_offset_georeferencingadded - oxigeo-drivers/grib: CRITICAL DRT 5.40 silent-corruption bug fixed — the GRIB2
decoder now dispatches on the Data Representation Template number, so a
JPEG2000/PNG/CCSDS payload can never fall through to the simple-packing
bit-unpacker; DRT 5.40 is wired to a real Pure-Rust JPEG2000 decode via
oxigeo-jpeg2000(new default-onjpeg2000feature) - oxigeo-shapefile (vector drivers): the Polygon reader now reconstructs
multi-part polygons by ESRI ring winding (clockwise = exterior, CCW = hole) with
containment-based hole assignment, emittingMultiPolygonfor multiple exteriors —
a two-island country shapefile round-trips instead of merging its rings - oxigeo-drivers/netcdf & oxigeo-drivers/hdf5: NetCDF-4 reader now recurses
into HDF5 sub-groups (was silently dropping their variables); the HDF5 writer's
chunking/compression/fill-value hints are no longer silently dropped (real chunked
write path plus honest errors for shapes oxih5 cannot represent); real object-header
parsing sodecode_chunk/filter-pipeline/chunking are no longer dead code - oxigeo-drivers/netcdf & oxigeo-drivers/hdf5: attribute decoding now trusts
the dataspace-declared element count (count × dtype_size) and ignores trailing
bytes, so scalar/small numeric attributes written with padded payloads no longer
decode as phantom extra elements — this silently disabled CF_FillValue/
scale_factorhandling for files written by oxih5 0.2.1, whoseFileWriterpadded
sub-8-byte scalar attribute payloads; the writer regression is now root-fixed
upstream in oxih5 0.2.2 (this workspace is pinned to it), and the defensive trim
stays in place as a belt-and-suspenders guard against older files written by 0.2.1 - oxigeo-drivers/geoparquet: XYZ/XYM geometry decode ambiguity fixed
Algorithms & CRS
- oxigeo (umbrella): CRITICAL
Dataset::clip()bug fixed — clip now records a
pixel window that every raster read (read_band/bands/statistics/convert/
read_window) crops the source file to, so a clipped dataset no longer silently
reprocesses the full raster - oxigeo-algorithms: real NEON SIMD (with scalar-parity tests) for morphology
(3×3 erode/dilate) and threshold kernels; a real CSE (let-binding hoisting) + DCE
(liveness/reachability) pass for the raster-algebra optimizer - oxigeo-proj: PROJ
+proj=hgridshift/+proj=vgridshiftpipeline steps now
actually apply a grid — newGridRegistry+Pipeline::with_hgrid/with_vgridand
evaluators calling the crate's NTv2 grid parser (a sign bug in it was fixed)
Server & OGC services
- oxigeo-server: the
/tiles/{layer}/{z}/{x}/{y}.{fmt}XYZ endpoint now renders
real raster data — reads the intersecting source window, reprojects Web-Mercator
tiles into the dataset's native CRS (per-pixel inverse warp for non-3857 data),
applies the layer colormap/RGB style, and masks off-dataset/nodata pixels as
transparent — replacing a hard-coded checkerboard - oxigeo-services: WPS
buffer/clip/unionnow perform real geometry math via
oxigeo-algorithmsand return the computed GeoJSON (previously ignored their
inputs); CQL2 gained!=/<>,IN (...), andIS [NOT] NULL
Query engine
- oxigeo-query / oxigeo-index: JOIN output now preserves native column types
instead of stringifying everything; SELECT projection lists are actually applied;
HAVING is executed (including aggregates referenced only by HAVING); the WHERE
evaluator gainedBETWEEN/IN/CASE/CASTwith real type coercion
ML
- oxigeo-ml: model pruning/quantization no longer corrupts ONNX files — a real
ONNX protobuf walker (optimization/onnx_weights.rs) applies genuine tensor
transforms;ModelVersionOrdbug fixed - oxigeo-ml-foundation: the crate now compiles and trains — a genuine trainable
scirs2-neural backend (real forward/backward/optimizer step with explicit gradient
routing) replaces code that referenced removedrandAPIs and mismatched types
Cloud & DB connectors
- oxigeo-postgis:
Transaction::dropnow issues a real implicitROLLBACK
(was a log-only message that leaked locks) with a double-take guard - oxigeo-db-connectors: MySQL/TimescaleDB SQL-injection surfaces closed via a new
crate::sqlidentifier-quoting/literal-escaping module plus parameter binding - oxigeo-cloud: CRITICAL rs3gw tokio nested-runtime panic fixed; byte-range reads,
the prefetch I/O driver, OAuth2/SAS credential refresh (HttpBackend), and STAC fixes - oxigeo-cloud-enhanced: fabricated Azure (Cost/Monitor/ML/Synapse) and GCP (Vertex
AI/Dataflow/Cost) clients replaced with real, bearer-token-authenticated REST clients
behind the existingazure/gcpfeatures — Azure Cost Management queries/forecasts/
budgets/Advisor, Azure Monitor metrics/Log Analytics/alerts/diagnostic settings, Azure
ML v2 control-plane compute/model/endpoint/job management, Synapse SQL/Spark pool (ARM)
management and Spark job/pipeline submission (Livy); GCP Dataflow template launch with
job status/list/metrics/cancel/drain, Vertex AI model/endpoint/training/batch-prediction
(long-running-operation polling), and GCP Cost Management via BigQuery billing export
plus Cloud Billing budgets/Recommender — every previously-fabricated success/ID/
empty-list is now a real call or an honest typedNotImplemented. True data-plane
operations a control-plane REST client can't mint stayNotImplemented(Monitor
metric/diagnostic ingestion, Cost alert/export, Synapseexecute_query, ML
invoke_endpoint, GCP cost forecast/export)
HA & infra
- oxigeo-ha: PITR, snapshot, backup, and DR were entirely fabricated (canned bytes,
always-pass tests) — replaced with real WAL + on-disk persistence and injectable
executors; a genuine Raft log-replication module (failover/log_replication.rs) with
AppendEntriesconsistency check, conflict truncation, and majority commit added - oxigeo-cluster (cluster-dist): leader heartbeats now travel over the transport to
followers (realAppendEntries-style RPC + handler) so followers stop perpetually
re-running elections; W-TinyLFU is now reachable and used by the multi-tier cache - oxigeo-kinesis / oxigeo-kafka / oxigeo-pubsub: fake/no-op broker paths
replaced with real implementations and honest errors — Firehose transformation now
actually happens; Kafka read-process-write exactly-once wired to real transactions
Bindings
- oxigeo-node: multi-band GeoTIFF save (BIP interleave round-trip); GeoJSON parser
handles every geometry type;CancellationTokenwired into batch/parallel processors
doing real chunked multi-threaded per-pixel work - oxigeo-jupyter:
%crs/%bounds/%statsnow read a real parsed GeoTIFF dataset
instead of returning hard-coded"(example)"literals - oxigeo-python:
open_raster/create_rasterno longer silently discard the
driver/optionsarguments — a real remote/cloud data-source layer (remote.rs)
wiresdriver="COG"and S3/HTTP options through tooxigeo-cloud
no_std & platform
- oxigeo-core / oxigeo-embedded: the no_std/embedded claim is now real
end-to-end — both crates genuinely cross-compile for bare-metal
thumbv7em-none-eabihf(Cortex-M4) andriscv32imac-unknown-none-elf(verified with
actual--targetbuilds);parking_lot/crossbeamare std-gated;RealtimeScheduler
deadline enforcement now actually fires - oxigeo-gpu / oxigeo-gpu-advanced:
reproject_gpu/execute_gpuno longer error
InvalidBufferat runtime — the output buffers now requestMAP_READusage
(verified on Metal) - oxigeo-proj: the
no_std(--no-default-features) build was broken — the crate
declared#![cfg_attr(not(feature = "std"), no_std)]but failed with 63 errors;extern crate allocis now unconditional and the alloc-prelude imports
(String/Vec/Box/ToString/format!) pluscore::f64::constsreplacements were
added across the crate, sono_stdgenuinely compiles and its tests pass
Release-verification pass
- oxigeo-cloud: a doctest in the multi-cloud abstraction example was missing a
#[cfg(feature = "s3")]guard, socargo test --docfailed to compile it under
default (non-s3) features - oxigeo-drivers-advanced: the GeoPackage doctest in
src/lib.rshad the same bug —
gpkg::GeoPackageused with no#[cfg(feature = "geopackage")]guard, because the
doc prose wrongly calledgeopackage"enabled by default"; fixed with the guard, the
prose, and afn example()/async fn example()in place offn main - 9
rustdoc::private_intra_doc_linksviolations fixed across 8 files in 7 crates —
oxigeo-index,oxigeo-gateway,oxigeo-security(×2 files),oxigeo-drivers/hdf5,
oxigeo-gpu,oxigeo-ml-foundation,oxigeo-postgis - Publish-order bug:
oxigeo-grib(its default-onjpeg2000feature depends on
oxigeo-jpeg2000) was sequenced beforeoxigeo-jpeg2000in both
~/work/pub_oxigeo.shandscripts/publish-order.txt— publishing in that order
would have failed with an unresolved dependency; both are now correctly ordered - 3 crates were missing
repositorymetadata:oxigeo-geojson-stream,oxigeo-index,
oxigeo-noalloc - oxigeo-node: npm
optionalDependencieswere still pinned to0.2.0while the
package itself is0.2.1 - Two hardcoded version strings in HTTP
User-Agentheaders (oxigeo-stac,
oxigeo-ml) replaced withenv!("CARGO_PKG_VERSION")so they can no longer drift
from the crate version
Added
- oxigeo-drivers/zarr: the empty Zarr v2 reader/writer stubs replaced with a working
v2 read/write path (chunk-key builder, compressor+filter pipeline, fill values,
dimension separator, dtype sizing); the ZEP-0002 v3 sharding codec; the fake ZFP codec
made honest (mode-honoring, overflow-checked) - oxigeo-drivers/geoparquet: the writer now emits real attribute columns and a
covering.bboxcolumn (was silently dropping all attributes); extended-WKB nested
geometry encoding; Hive-style + spatial (bbox-grid/quadtree/Z-order) partitioning - oxigeo-geotiff: real LERC decode (BitStuffer2 v1/v2/v3) and a JPEG-in-TIFF read
path that auto-merges sharedJPEGTables(tag 347) - oxigeo-proj: native forward/inverse projections + round-trip tests for Equidistant
Conic, Sinusoidal, Mollweide, Robinson, Eckert IV/VI, Cassini-Soldner, and
Gauss-Krüger (extended zones) - oxigeo-drivers/grib: template-based product-definition expansion (PDT 0.0–0.48
coverage) and NetCDF CF-conventions v1.11 parsing (cf_conventions/v1_11.rs) - oxigeo-gpu: reprojection, raster-algebra, and hillshade WGSL compute shaders;
multi-GPU workload distribution; WebGPU/WASM shader compilation via a compile-time
ShaderRegistry - oxigeo-ml: ONNX model hot-reload (file-watch + atomic swap), content-addressed
inference caching (SHA-256 key + LRU), adaptive batch sizing, and model
versioning / deterministic A/B testing - oxigeo / oxigeo-streaming:
DatasetOpenBuilder/DatasetCreateBuilderfluent
builders; aFeatureStream/TileStreamstreaming-iterator API - oxigeo-mbtiles / oxigeo-gpkg / oxigeo-pmtiles: a real SQLite-backed MBTiles
writer (now genuinely persists to.mbtiles); an opt-in R-tree spatial-index writer
for GeoPackage - fuzz/: 7 new libFuzzer targets (NetCDF, HDF5 superblock/object-headers, VRT XML,
GeoJSON, and more), bringing coverage to 11 format/parser targets - tests/: the 1,337-line mock re-implementation in
vector_advanced.rsreplaced —
33 tests now exercise the realoxigeo-algorithmsvector stack - oxigeo-gateway serving layer: the previously stubbed
Gateway::serve()(it accepted
TCP connections and itshandle_connectiondid nothing) is now a real axum 0.8 HTTP
service — a newGatewayServer/GatewayServerBuilderwires the crate's
already-implemented components into a running router:- routes:
GET /health,GET /gateway/metrics,POST /graphql(plus a GraphiQL page
when introspection is enabled and a/graphql/wssubscription endpoint when
enable_subscriptionsis set — that flag is now actually enforced), aGET /ws
WebSocket upgrade (WebSocketManager wiring, defaultEchoHandlerroute, per-user
connection caps, ping keepalive, gated onenable_websocket), and a load-balanced
reverse-proxy fallback - reverse proxy: a streaming hyper 1 connection client, HTTPS upstreams over the
Pure-Rust OxiTLS (rustls/RustCrypto) probe connector, hop-by-hop header stripping,
FailoverManagerretries that finally honor the previously-ignored
LoadBalancerConfig.retry_attempts, circuit-breaker outcome reporting, and per-attempt
request timeouts - pipeline: query-free trace spans (no query strings), API version negotiation +
deprecation headers, the in-house middleware chain (CORS with realOPTIONSpreflight,
compression, response caching, logging, metrics), JWT/API-key/session auth via
MultiAuthenticator(authenticate-if-present plus arequire_authmode, with the
require_mfaflag now enforced), atomic rate limiting withX-RateLimit-*/
Retry-Afterheaders, request timeout and body-size limits; arequire_permission
RBAC guard is available for route groups andGatewayErrornow implements
IntoResponse - honesty fixes:
CachingMiddlewareis now a real LRU+TTL cache instead of a no-op stub;
compression performs realAccept-Encodingnegotiation; the 1,865-line
middleware::advancedmodule (request-ID / enhanced-logging / timeout-header /
error-handling / histogram-metrics / cache-control) was orphaned — never declared or
compiled — and is now wired in, compiling and tested;X-Forwarded-Foris built
against a trusted-proxy allowlist (with_trusted_proxies) rather than blindly trusting
client-supplied values - honest limitations (v0.3.0+): GraphQL resolvers still serve demo/in-memory data (no
storage backend); middleware-chain hops and proxied requests are buffered (bounded by
max_body_size) while proxy responses stream; there is no WebSocket pass-through
proxying, no upstream keep-alive pooling, and response-side transformation is not yet
wired (request-side only) - the crate's own test suite grew from 266 to 381 tests (1 → 3 doctests)
- routes:
Security
- oxigeo-services: WFS-T CQL filtering now fails closed on unparseable CQL —
an unparseable filter previously failed open, matching every feature and enabling a
mass delete/update; it now rejects the request - Memory-safety (DoS/OOM hardening): header-driven allocation caps added to the
NetCDF, HDF5, GRIB, and GeoTIFF parsers so a crafted header can no longer trigger a
multi-gigabyte allocation; includes the GeoTIFFusize-overflow fix noted above - oxigeo-gateway: load-balancer health checks now issue genuine HTTP/1.1-over-TCP
requests (real Pure-Rust TLS via the OxiTLS RustCrypto provider for HTTPS) instead of
always returning healthy, so a down backend is correctly marked unhealthy; the
MalwareScannernow actually reads and inspects its input; the gRPC health check
fails closed with an honest error rather than reporting unknown backends healthy - oxigeo-observability: health checks do real work (sysinfo disk usage, injectable
connectivity checker) instead of returning hard-codedHealthy; a stubLabelMatch
alert condition that always returnedtruefixed
Changed
- oxigeo-db-connectors: default features made Pure-Rust — the C-FFI database backends
are now strictly opt-in behind named features - oxigeo-query:
tokiomoved to dev-dependencies andrayongated behind a
default-onparallelfeature, so the SQL engine is consumable from
wasm32-unknown-unknown - Packaging & legal: added
NOTICEandTHIRD_PARTY.md(Apache-2.0 §4(d)
attribution + generated third-party license inventory), a committeddeny.toml
(advisories + bans + licenses) wired intocargo deny check, an in-repo 75-crate
topological publish-order manifest (previously only in an external script), a license
note for the vendoredpathfinder_simd, and[package.metadata.docs.rs]fixes on
the C-FFI-gated crates - Supply-chain hygiene:
.cargo/audit.toml's advisory allowlist re-verified against
the current lockfile and pruned from 21 to 15 entries —aws-lc-sys
(RUSTSEC-2026-0044/-0048) andtokio-postgres/postgres-protocol
(RUSTSEC-2026-0178/-0179/-0180) are already patched at our pinned versions, and
proc-macro-error2(RUSTSEC-2026-0173) is no longer in the dependency graph; the new
deny.toml[bans]list enforces this workspace-wide, andtower-http's
compression-br/compression-gzip/compression-deflatefeatures (unused — no
CompressionLayeranywhere — but pulling bannedflate2/brotli/miniz_oxideoutside
deny.toml's allowed wrapper scoping) are now explicitly excluded in every consumer;
SECURITY.md's contact address corrected tosecurity@cooljapan.tech - Dependencies kept current per the Latest Crates Policy (
arrow58 → 59,indicatif
0.18 dropping the unmaintainednumber_prefix,oxih5/oxih5-core/oxinetcdf
0.2.0 → 0.2.2,scirs2-coreand thescirs2-{neural,autograd,optimize,datasets, metrics,linalg,vision,series}family 0.6.1 → 0.6.4) - A further round of Latest Crates Policy bumps:
base640.22 → 0.23,pollster
0.4 → 1.0,las0.9 → 0.10,jsonwebtoken10 → 11,ed25519-dalek2 → 3
(stdfeature dropped,zeroizeretained),azure_core1.0 → 1.1,
google-cloud-pubsub1.1 → 1.2,statrs0.18 → 0.19,tokio-tungstenite
0.29 → 0.30. Onlylas0.10 required a source change: it replaced the
per-pointReader::points()streaming iterator with a batch/buffer API
(Reader::read_all()/read_points(n)returning aPointDataslab whose
.points()yields the same row-oriented iterator), sooxigeo-3d's
LasReader::read_all/read_nwere updated accordingly; the other eight
bumps were drop-in with no source changes required - Dependency hygiene: genuinely-unused dependencies removed from 66 crates'
Cargo.tomlfiles (found viacargo-machete, each removal build-verified);
deny.toml's advisory-ignore list pruned from 15 to 7 entries (the other 8 IDs no
longer match anything in the currentCargo.lock) and its license allowlist trimmed
of entries no longer reachable in the dependency graph; awildcard-dependency
cargo-denywarning resolved viaallow-wildcard-paths(three intra-workspace
dev-dependencies —oxigeo-3d→oxigeo-copc,oxigeo-dev-tools→
oxigeo-algorithms,oxigeo-qc→oxigeo-geojson— are deliberately unpinned path
deps so publish ordering doesn't become circular)
Removed
-
oxigeo-kafkais retired as a project, effective 0.2.1. The crate has been
deleted from the workspace and will receive no further releases; the versions
already on crates.io (0.0.1 and 0.2.0) have been yanked. This is a deliberate
retirement, not an oversight — the crate is gone on purpose and is not coming back.Removed alongside it: the
kafkafeature of oxigeo-etl (and with it
KafkaSource/KafkaSourceConfig,KafkaSink/KafkaSinkConfig, their prelude
re-exports, and theKafkavariants ofSourceError/SinkError), thekafka
feature of oxigeo-workflow (which gated anrdkafkadependency that no source
file in that crate ever used), and therdkafkaentry in[workspace.dependencies].Reason:
oxigeo-kafkawas the sole mandatory C-toolchain dependency in the entire
workspace —rdkafka-sysbuilds librdkafka viacmake— which stands against the
COOLJAPAN Pure Rust Policy. At 4,831 lines it was 0.62% of the workspace's ~778k
lines of Rust and had zero reverse dependencies inside the workspace: nothing
built on it. As a direct result of the removal,cargo check --workspace --all-featuresno longer requirescmakeor a C toolchain and completes clean.Migration: use a dedicated Kafka client (e.g.
rdkafka) directly in your own code,
or one of the sibling messaging crates that remain supported —oxigeo-streaming,
oxigeo-kinesis,oxigeo-pubsub,oxigeo-mqtt. Workflow definitions can still
describe a Kafka endpoint over the wire: the pure-RustIntegrationType::Kafka
andMessageQueueType::Kafkametadata enums inoxigeo-workfloware unchanged. -
oxigeo-proj: the
proj-sysfeature and theprojC-bindings dependency (C
bindings to the system libproj) removed, per the COOLJAPAN Pure Rust Policy. All
coordinate transformation already routed through the pure-Rustoxiprojengine, so
the feature was vestigial — it contributed only an unused error variant and its
From<proj::ProjError>conversion, and no transformation path ever called the C
library. Its one real effect was that--all-featuresbuilds requiredcmakeand a
system libproj (theprojcrate builds PROJ from source), which broke
cargo test --workspace --all-features. For higher-fidelity CRS coverage use the
pure-Rustproj-dbfeature (oxisql PROJ.db reader, ~7500 EPSG codes) instead.
Full Changelog: v0.2.0...v0.2.1