# JSTprove: zero-knowledge proofs for ONNX model inference

> A Rust and Python toolkit that turns an ONNX model into an arithmetic circuit, generates a witness, proves the computation and verifies it, with the prover itself checked into the tree.

**inference-labs-inc/JSTprove** — JSTprove - Fast, verifiable AI

- Repository: https://github.com/inference-labs-inc/JSTprove
- Stars: 1,321 · Forks: 15
- Language: Rust
- License: NOASSERTION
- Published: 2026-10-06 · Updated: 2026-10-06 · Language: en
- Canonical page: https://hysenlabs.com/projects/inference-labs-inc-jstprove

## What a proof of inference actually means here

JSTprove is described as a zkML toolkit and CLI that produces zero-knowledge proofs of AI inference. The input is an ONNX model plus inputs; the output is a proof that the model ran and produced the claimed result, without revealing the inputs.

The pipeline is stated as a chain of five stages in the README, and the architecture diagram makes the boundaries explicit:

```
ONNX model ─► Circuit via ECC (Rust) ─► Witness (Rust) ─► Proof (Rust) ─► Verify (Rust)
```

So quantization happens first, the model becomes an arithmetic circuit, a witness is generated from the inputs, the prover produces a proof, and a verifier checks it. Each arrow is a Rust binary or crate boundary, and each stage emits a concrete file you can inspect.

The cryptographic machinery is not abstract. The prover backend is identified as Polyhedra Network's Expander, described as a GKR and sum-check prover and verifier. GKR is the protocol that makes proving an arithmetic circuit cheap relative to the circuit's size, and sum-check is the argument that keeps each step of that protocol sound. The circuit frontend is the ECC Rust API for arithmetic circuits, from the same organisation.

The design principles section is unusually candid about tradeoffs. The stated goal is a user-friendly frontend to Expander: a thin, practical, circuit-based layer, with no circuit classes to learn, no path inference, and predictable artifacts. The second principle is explicit and reproducible paths, since you pass exact paths and the tool emits a compiled circuit, a witness and a proof with no hidden discovery or heuristics.

## Eighteen operators, and quantization changes your model

The README lists the supported operations exactly: Add, BatchNormalization, Clip, Constant, Conv, Div, Flatten, Gemm, Max, MaxPool, Min, Mul, ReLU, Reshape, Squeeze, Sub, and Unsqueeze. That list is the real scope of the tool, and it is short. A model using a softmax, a concatenation, a transpose or any attention operation is not currently provable.

What is on the list covers a convolutional classifier and a small dense network well, which is why the demo the README points at is LeNet. If you are evaluating this for a transformer, the operator list is the first thing to check, and it is worth counting the distinct ops in your own model before anything else.

Quantization is the second thing to understand, and the README is precise about the method. Tensors are scaled, rounded to integers, the model runs, and where needed the outputs are rescaled back. The stated rationale is that scaling keeps arithmetic cheap while remaining close to the original floating point behaviour.

That phrasing contains its own caveat. A proof attests to the quantized computation, not to the floating point model. Anyone verifying the proof confirms that the circuit ran on specific inputs and produced specific outputs, which is a meaningful guarantee, but it is a guarantee about the rounded model. If your use case requires that the exact float computation happened, this is not that tool.

The last design principle is circuit size. Where it is safe, common patterns are fused into streamlined fragments to reduce constraints, with Linear plus ReLU and Conv plus ReLU given as the examples. Fewer constraints means a faster proof, and fusing adjacent operations is the cheapest available win.

## A vendored prover and five field backends

The `Cargo.toml` workspace member list is the most informative file in the repository, and it is surprising. A typical application would depend on a prover. Here the prover is the repository.

The workspace includes `prover/gkr` and `prover/gkr_engine` for the protocol itself, `prover/sumcheck` for the sum-check argument, `prover/poly_commit` for polynomial commitment, `prover/transcript` for the Fiat-Shamir style transcript, `prover/hasher` for hashing, `prover/circuit` and `prover/tree` for the circuit representation, `prover/serdes` with a derive macro for serialization, `prover/config_macros`, `prover/crosslayer_prototype`, `prover/utils` and `prover/bin`.

Then there are the arithmetic backends, which is where the field choices become visible: `babybear`, `gf2`, `gf2_128`, `goldilocks` and `mersenne31`. These are five different finite fields, each appropriate to a different proof system. Goldilocks is the field used by Plonky3-style constructions, and the presence of several says the workspace is built to be reconfigured rather than fixed to one protocol.

Two more entries stand out. `prover/metal_accel` is Metal GPU acceleration, so there is a hardware-accelerated path for Apple platforms. And `compiler/expander_compiler` with its macros, plus `compiler/circuit-std-rs`, is the circuit construction layer that sits between ONNX and the prover.

The application crates are the four named in the README, `jstprove_circuits`, `jstprove_io`, `jstprove_onnx` and `jstprove_remainder`. Three exclusions are declared: `rust/jstprove_pyo3`, `rust/jstprove_zkvm` and `.claude`. The `jstprove_remainder` crate and the second CLI binary named in the README, `jstprove-remainder`, indicate a second backend alongside Expander.

There is a versioning wrinkle worth flagging. The Cargo workspace package version is 0.4.0, `pyproject.toml` declares 0.1.0 for the Python package, and the release tags are on a 2.x line with v2.12.1 as the most recent. Three different version numbers in one repository means you should check which one your wheel actually corresponds to.

## Installing the wheel with uv

The recommended path is a Python package installed from PyPI, and it requires uv as the package manager. The command is short:

```bash
uv tool install JSTprove
```

Verification is equally short:

```bash
jstprove --help
```

There is also a route from a GitHub release wheel, for platforms where the PyPI build is not what you want. The README names the two wheel patterns it publishes: Linux as `JSTprove-*-manylinux_*.whl` and Apple Silicon macOS as `JSTprove-*-macosx_11_0_arm64.whl`, installed with `uv tool install /path/to/JSTprove-*.whl`. Only those two platforms are documented, which is worth knowing if you are on Windows or on Intel macOS.

The Python side of the package is thin by design. `pyproject.toml` uses maturin as the build backend with the `pyo3/extension-module` feature, the Python source lives in `pysrc/`, and the extension module is `jstprove._native`, built from the manifest at `rust/jstprove_pyo3/Cargo.toml`. The dependencies are `msgpack`, `onnx` at 1.19.1 or later, and `onnxruntime` at 1.20.1 or later, with Python 3.9 as the floor. onnxruntime being a hard dependency confirms the flow really does execute the model, since witness generation needs concrete outputs to commit to.

The bindings expose `Circuit`, `WitnessResult` and `BatchResult` according to the README, which maps directly onto the pipeline stages.

Development installation is heavier, requiring a Rust toolchain and system libraries. The `rust-toolchain.toml` file pins the required nightly, and the README points out that you do not need to run `rustup override set nightly` because rustup reads that file automatically. On Debian or Ubuntu the system packages are:

```bash
sudo apt-get update && sudo apt-get install -y \
  pkg-config libclang-dev clang
```

On macOS the equivalent is `brew install llvm`.

## Conclusion

JSTprove is an honest front end to a substantial piece of cryptography rather than a thin wrapper, because the GKR prover, the sum-check implementation, the polynomial commitment scheme and five arithmetic field backends are all part of this repository's Cargo workspace rather than external dependencies. What you give up is operator coverage: the supported list is eighteen operations and stops there, so a model using anything outside it is not provable yet. Two practical notes before starting. First, the arithmetic is integer after quantization, so proving a model means accepting a rescaled approximation of it rather than the original float computation. Second, the repository pins a nightly Rust toolchain through `rust-toolchain.toml`, which removes a class of version problems and introduces a nightly dependency. Start with the LeNet demo the README points to, since a model with convolution and pooling covers most of what the supported operator list contains.

## FAQ

### What does JSTprove prove?

It proves that an ONNX model was executed on specific inputs and produced specific outputs, without revealing the inputs. The pipeline quantizes the tensors, compiles the model into an arithmetic circuit through ECC, builds a witness, generates a proof with Expander's GKR and sum-check prover, and then verifies it.

### Which ONNX operators does JSTprove support?

Eighteen as listed in the README: Add, BatchNormalization, Clip, Constant, Conv, Div, Flatten, Gemm, Max, MaxPool, Min, Mul, ReLU, Reshape, Squeeze, Sub and Unsqueeze. Anything outside that list is not currently provable, so counting the distinct operators in your model is the first step before evaluating the tool.

### Does JSTprove prove floating point inference exactly?

No. The README states that tensors are scaled and rounded to integers, the model is run, and outputs are rescaled back where needed. A proof therefore attests to the quantized circuit rather than the original floating point computation, which is a meaningful guarantee but a different one from proving the exact float model.

### How do I install JSTprove?

With uv, from PyPI, using `uv tool install JSTprove`, then confirm it with `jstprove --help`. Wheels from the GitHub release are also supported with `uv tool install /path/to/JSTprove-*.whl`. The README documents Linux manylinux wheels and Apple Silicon macOS wheels specifically. A development install needs a nightly Rust toolchain plus libclang.

### Is the zero-knowledge prover an external dependency of JSTprove?

Mostly not. The Cargo workspace in this repository contains the GKR prover, the GKR engine, sum-check, polynomial commitment, the transcript, hashing and the circuit layer, along with five arithmetic backends: babybear, gf2, gf2_128, goldilocks and mersenne31. Expander is the named backend, and the tree also contains a `jstprove-remainder` crate and binary suggesting a second backend.

## Sources

- [inference-labs-inc/JSTprove on GitHub](https://github.com/inference-labs-inc/JSTprove)
- [Issues](https://github.com/inference-labs-inc/JSTprove/issues)
- [README](https://github.com/inference-labs-inc/JSTprove/blob/main/README.md)
- [Releases](https://github.com/inference-labs-inc/JSTprove/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/inference-labs-inc-jstprove
