A 232-finding audit is either a scandal or a release note, depending entirely on whether you went looking for the findings yourself.
Today we released OxiONNX 0.1.5 — a production-hardening release for the COOLJAPAN Pure Rust ONNX inference engine. A 12-lens audit (spec conformance ×3, engine, proto robustness, panics, GPU, CUDA/CoreML, stubs, API/release, performance, test gaps) covering the whole workspace produced 232 findings, fixed across three waves of parallel implementation under strict file-ownership partitioning — alongside three new runtime capabilities: async execution, cancellation tokens, and streaming token generation for autoregressive models.
No C. No C++. No ONNX Runtime binaries. No external protobuf or CUDA dependencies.
No unsafe code outside a handful of documented, audited call sites.
Just memory-safe ONNX model execution that compiles to a single static binary (or WASM) and runs everywhere — from laptops to browsers to edge devices to GPUs.
Why OxiONNX 0.1.5 is a game changer
A production hardening pass is where an inference engine earns trust or loses it — a wrong answer that never panics is worse than a crash, because nothing tells you it happened. This release’s 12-lens audit went looking for exactly that class of bug across the whole workspace:
- Parser paths that trusted length fields from untrusted
.onnxbytes without checking them against the buffer — including one fully unguarded slice in the streaming reader - Spec-conformance gaps where an operator silently computed the wrong result instead of erroring:
Padclamped negative pads to zero instead of cropping,Resizesilently degraded cubic interpolation to nearest-neighbor,GRUmisapplied the reset gate on the ONNX defaultlinear_before_reset=0 - GPU/CUDA/DirectML dispatch paths that could silently produce wrong output on shapes the kernel didn’t actually support, instead of declining to the CPU fallback
GpuTensorTracker— a public type whose docs promised GPU-resident tensors between operations, when nothing outside its own unit test ever calledstore/take/is_on_gpu
OxiONNX 0.1.5 ends all of that.
- 232 findings closed across parser hardening, spec-conformance, GPU/CUDA/DirectML dispatch, and the core tensor layer — verified by 2,946 passing tests
- 23 new operators land the registry at 188 (203 op-type strings including aliases), up from 165: the QOperator quantization family (
QLinearConv,QLinearMatMul,MatMulInteger,ConvInteger,DynamicQuantizeLinear), classic-CNN ops (LRN,LpPool,MaxUnpool,MaxRoiPool,Upsample),Det/Col2Im/CenterCropPad, theRandom*generator family, and two new loss ops DFT/STFTcorrectness fix on non-AVX2 x86_64 — an upstreamoxifftSIMD butterfly bug that made outputs wrong by orders of magnitude, not just imprecise, on every SSE3-only x86_64 hostdecide_placementis now the single source of truth for CPU/GPU/CUDA/DirectML routing, replacing three previously-disagreeing op-support lists that let implemented GPU kernels go unreached from theAutoplacement path
Technical Deep Dive: three new runtime primitives, one routing rewrite
- Async execution (
src/session/async_run.rs) —Arc<Session>::run_async(inputs) -> RunFuturestarts the model on its own thread immediately and returns a future you can.awaitunder tokio/async-std/smol, or drive with the crate’s own dependency-freeblock_on.spawn_run()returns a blockingRunHandlefor callers with no async executor at all. - Cancellation tokens (
src/session/cancellation.rs) —SessionBuilder::with_session_cancellation(token)makes every operator check aCancellationTokenbefore it runs, unwinding withOnnxError::Cancelledat the first node boundary — on the sequential path, the rayon parallel path, and insideIf/Loop/Scanbodies. - Streaming generation + session serialization (
src/streaming.rs,src/session/serialize.rs) —session.generate(prompt, config)runs one forward pass pernext(), feedingpresent.*key/value outputs back in as the next step’spast.*inputs.session.save_optimized(path)persists the post-optimization graph so constant folding/CSE/fusion never re-run on load — proven by counting operator executions during load: zero. - Placement unification (
src/execution_providers.rs) —select_accelerator(op)now consults each backend’s own op-support predicate in priority orderCuda > DirectMl > Gpu, with a hard floor (MIN_GPU_DISPATCH_BYTES, 4096 bytes) below which even an explicit accelerator pin falls back to CPU, since a discrete-GPU round trip costs more than the op it would run.
Getting Started
cargo add oxionnx
The core inference API is unchanged in 0.1.5:
use oxionnx::{Session, Tensor};
use std::collections::HashMap;
let session = Session::from_file("model.onnx".as_ref())?;
let mut inputs = HashMap::new();
inputs.insert("input", Tensor::new(vec![1.0, 2.0, 3.0], vec![1, 3]));
let outputs = session.run(&inputs)?;
println!("{:?}", outputs);
New in 0.1.5 — streaming, token-by-token generation. There’s no tokenizer built in: token ids in, token ids out.
use oxionnx::{Session, GenerationConfig};
let session = Session::from_file("model.onnx".as_ref())?;
let prompt: Vec<i64> = vec![1, 464, 3290]; // from your own tokenizer
let config = GenerationConfig::default()
.with_max_new_tokens(64)
.with_eos_token_id(2);
for step in session.generate(&prompt, config)? {
let step = step?;
print!("{} ", step.token);
}
What’s New in 0.1.5
- Added: async execution (
run_async/spawn_run/block_on), session-scoped and per-generation cancellation tokens, streaming token generation (session.generate), session serialization (save_optimized/load_optimized), ONNX local-function inlining fixed for the streaming parser, opset-awareSoftmax/LogSoftmax/Hardmax, rank-genericConv/ConvTranspose(1D/2D/3D), rank-0 scalar tensor support, 23 new operators, einsum ellipsis and cross-operand broadcasting routed throughmatrixmultiply::sgemm,#[non_exhaustive]on public error enums, a wave of CoreML additions (predict_raw,predict_features,compute_plan_breakdown,model_metadata, iOS/tvOS/visionOS support), and a real D3D12/DirectML backend replacing the prior Windows skeleton. - Changed: CPU performance work (small-M
MatMuland attention routed throughsgemm, in-place KV-cache append, threaded convolution), GPU performance work (device-reported limits instead of conservative defaults, a merged Softmax pipeline, a 256 MiB LRU buffer pool),oxionnx-gpu’s deadGpuTensorTrackerAPI removed, OxiCUDA deps 0.1.8 → 0.5.3,oxifft0.3.2 → 0.4.2,aes-gcm0.10 → 0.11. - Fixed: parser bounds-checking and recursion depth limits,
Slice/Pad/Resize/GRU/ScatterElements/ScatterND/QuantizeLinear/TreeEnsemblespec-conformance bugs, theoxifft-inherited DFT/STFT SIMD correctness bug, GPU/CUDA/DirectML dispatch declining instead of guessing on unsupported shapes, wasm32 GPU context creation honestly declining instead of doing wasted work, a topological-sortusizeunderflow, CSPRNG-sourced encryption nonces, and anAttributeProto.stringsmis-parse that brokeTreeEnsemblemodels (issue #3).
Tips
- If you’re on a non-AVX2 x86_64 host, re-check any
DFT/STFTresults you computed before this release. The bug lived inoxifft’s SSE3 butterfly kernel (fixed upstream in 0.4.2, pulled in here) and produced outputs wrong by orders of magnitude, not just imprecise — aarch64 and AVX2+ hosts were never affected. session.generate()takes and returns token ids, not text. There’s no tokenizer in the crate by design; encode your prompt and decodeStreamStep::tokenyourself. SetGenerationConfig::with_emit_logits(true)if you need the raw logits row a token was chosen from — it’s off by default because a 50k-vocabulary logits row is a 200 KB copy per token.run_asyncspawns a thread per call — it’s for one long inference, not many small concurrent ones. For a batch of small requests, plainrun()behind your own worker pool will beat one OS thread per request.- Cancellation is session-scoped by default.
SessionBuilder::with_session_cancellation(token)cancels every run in flight on that session; if you only want to cancel one generation on a session shared across requests, useGenerationConfig::with_cancellationinstead, checked between decode steps. save_optimized/load_optimizedskip optimization entirely on load. If you rebuild a cached graph against a different execution-provider configuration than the one that produced it, re-optimize from the original model instead of trusting the cache — runtime settings like threads, providers, and profiling are deliberately not part of what gets cached.- New GPU/CUDA/DirectML routing means some ops that used to silently run on GPU now decline to CPU.
decide_placement’s unification is stricter than before —Conv, for example, is never routed to CUDA (no CUDA convolution kernel exists) even though wgpu’s capability check claims it. If a hot path regresses, checkprovider_supports_opfor that op/backend pair before assuming a bug.
This is the foundation
OxiONNX is the ONNX inference backend across the COOLJAPAN stack — all bumped to oxionnx = "0.1.5" as part of this release:
- VoiRS — multimodal speech+vision inference
- OxiMedia — vision model deployment
- OxiWhisper — speech-model inference
- OxiGeo — geospatial ML inference
- ToRSh — model-hub inference integration
- OxiFY — LLM workflow orchestration
- OxiFace — face-model inference pipelines
- Oxi3D — 3D/graphics model inference
- OxiBonsai (
oxionnx-proto) — protobuf model parsing
Repository: https://github.com/cool-japan/oxionnx
Star the repo if you want an ONNX runtime where the hardening work is public — all 232 of these findings and their fixes are in this release’s history, not a private incident report.
The era of trusting a “just works” ONNX runtime you can’t audit is over.
Pure Rust ONNX inference — hardened, sovereign, and inspectable end to end — is here.
— KitaSan at COOLJAPAN OÜ August 7, 2026