CLI tool
o1-labs/o1js avatar
o1-labs/o1js

o1js: writing zero knowledge circuits in TypeScript

TypeScript framework for zk-SNARKs and zkApps

605 stars172 forksTypeScriptApache-2.0

At a glance

What is it?
o1js is a TypeScript framework for building zk-SNARK circuits and zkApps, letting a developer write a circuit, prove it in a browser or in Node, and produce proofs for the Mina Protocol to verify. It ships a compiler chain, WebAssembly bindings for node and web, a scaffolding CLI, and an ecosystem of community packages including a static analyser for prover-controlled witness bugs.
Who is it for?
Adopt o1js if you are building a zkApp for the Mina Protocol and want to write circuits in TypeScript rather than in a domain specific proving language, and if you want the browser as a first class proving target. Do not adopt it as a general zero knowledge toolkit, because it is tied to Mina's field, proving system and verifier, and the compile and prove cycle is slow enough that it shapes how you work.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Circuits in a language your team already writes

o1js makes it easy for developers to build applications powered by zero-knowledge cryptography.

The concrete scope is in the next paragraph. Developers can use o1js to write zk circuits and prove plus verify them in a browser or node environment, or create proofs to be verified by the Mina Protocol. Three things follow from that sentence, and they define the project.

First, the output is a proof, not a computation. A zk circuit is a program whose execution you can convince a verifier happened correctly without revealing the inputs, and a zkApp is an application whose interesting part is that property. The verifier here is the Mina Protocol, so the framework is aimed at a specific chain rather than at zero knowledge in the abstract.

Second, the proving happens on the client. A browser or Node environment means the person generating the proof does the expensive work, and the chain only checks the result. That is the architecture that makes private inputs possible, and it is why the browser target is a first class build rather than an afterthought.

Third, the language is TypeScript. The package is published as o1js on npm, the description calls it a TypeScript framework for zk-SNARKs and zkApps, and the type is module, so the package is ESM first.

The licence is Apache 2.0, the homepage is o1js.dev, and the recent releases are v3.1.0 from 2026-10-02, v3.0.0 from 2026-08-18 and 2.15.0 from 2026-05-06, with the last push on 2026-10-13. The jump from 2.15.0 to 3.0.0 in August 2026 is the kind of major version bump that a compiler framework goes through when its internals change, and the changelog is linked for the detail of what changed between versions.

The package ships a compiler, a runner and two sets of bindings

The manifest shows that o1js is not a thin wrapper around a cryptographic library. It is a build toolchain with a binary, which is why the files list and the scripts are so long.

The published files include src/build, dist, the TypeScript sources and their declaration files, and the source maps, so consumers get the sources as well as the compiled output. The bin entry is snarky-run, pointing at src/build/run.js, which is the executable for running o1js code.

The exports map has separate entry points per environment. The browser condition resolves to dist/web/index.js, and the node condition has both an import at dist/node/index.js and a require at dist/node/index.cjs, with the default falling back to the web build. The types resolve to dist/node/index.d.ts. So a bundler that respects the browser condition gets the web build and a Node process gets the node build with a CommonJS fallback, and the difference between them is the difference between proving in a page and proving on a server.

The build scripts are the interesting part. There are separate shell scripts for a dev build, the node build, the web build and the examples, and a family of bindings scripts: build bindings for node, update all bindings, build bindings remotely, download bindings, and build the transaction layout. There is also a check-for-bindings script, which is the shape of a project where a generated artefact has to be kept in step with an upstream.

The bindings are the reason. A zk framework needs to talk to a prover and a verifier, and the prover is not written in TypeScript, it is a compiled library with a native or WebAssembly interface. The bindings scripts exist to build or fetch that interface for each target, and the wasm build scripts exist because the browser target needs the prover compiled to WebAssembly.

The engine requirement is Node 22.19.5 or newer, and there is a run directory in the repository root alongside the run scripts, plus a flake.nix and a dune-project, which is the sign that part of the toolchain is Rust and Dune based.

The scaffolding CLI, and the docs are split by audience

Getting started is delegated to a separate package, and the readme is explicit about which tool does what.

The zkApp CLI is a scaffolding tool to start a new project with o1js, published on npm as zkapp-cli. So the CLI creates the project skeleton and the framework itself is what you then write against.

The documentation is split by what you are trying to learn. To learn more about the language features, see the o1js documentation at docs.o1labs.org. To learn how to build zkApps on Mina, see the zkApp documentation at docs.minaprotocol.com. That split is a real signal about the two audiences: the language is the framework's own business, while the application model, the chain, the account structure and the proving workflow belong to the Mina documentation.

The community links are the third group. There is a Discord channel for zkApp developers on the Mina Protocol server, the changelog for changes between versions, and the o1Labs blog for staying up to date with o1js specifically.

For anyone working on the library itself, the development guidance is in a separate README-dev document rather than in the main readme, and the repository root shows how much machinery is involved: a run directory with shell scripts for the different test suites, run-all-local-tests.sh plus separate scripts for unit, integration, CI, CI live and CI benchmark runs, a Playwright config for browser tests, a Jest config, an audits directory, a benchmark directory, a generate-keys.js script and a pin.sh script, plus a .husky directory for git hooks and an .oxlintrc.json.

That is a much larger surface than a pure library, and it is the honest shape of a framework that compiles circuits and manages prover bindings.

Community packages, including one that hunts circuit soundness bugs

The readme lists five community packages, and one of them is worth more attention than the others.

o1js-elgamal is a partially homomorphic encryption library for o1js based on Elgamal encryption. o1js-pack is a library that lets a zkApp developer pack extra data into a single Field, which is a Mina specific trick and the kind of thing you discover by hitting a limit. zk-regex-o1js is a CLI tool for compiling ZK Regex circuits in o1js, so a regular expression can be turned into a circuit. eddsa-o1js is a library for provable EdDSA and Twisted Edwards Curve operations.

Then there is o1js-scan, a static analyzer for zk circuit soundness bugs in o1js zkApps and Noir circuits, and the description is precise: it flags prover-controlled witnesses that the circuit never binds.

That is the single most important idea in this list, and it deserves an explanation. In a zk circuit, a witness is the private input the prover supplies. If the circuit computes an output without constraining the witness to be related to that output, then the prover can supply whatever witness makes the output true, and the proof still verifies. The result is a proof of nothing. A circuit that forgets to bind a witness is sound in the sense that it compiles, runs fast and produces a valid proof, and it is useless.

This failure mode is hard to catch by testing, because a test that uses the honest witness passes. It is caught by reading the circuit, or by a static analyser that looks for outputs that are not constrained. The fact that a community package exists for it, and that it covers Noir as well as o1js, says the problem is common enough in the ecosystem to justify a dedicated tool.

The readme also points to the contributing guidelines section on creating high-quality community packages, so the list is curated rather than accidental.

What the compile and prove cycle costs you

The practical cost of the approach is not stated in the readme, and it is the thing you will feel first.

A zk circuit is compiled, and then a proof is generated, and the generation is the expensive part. In a browser, the prover runs as WebAssembly, which is why the build has a wasm target for both web and node, and why the bindings have to be built for each of them. The generation of a proof is orders of magnitude slower than executing the same logic normally, and the compilation of the circuit adds a step that does not exist in conventional application development.

That has a direct effect on how you develop. You cannot iterate on a circuit the way you iterate on a function, because each edit means a recompile and each run means a proof. The common practical consequence is that you keep the circuit small and you push work out of the circuit into ordinary TypeScript wherever the privacy property allows it, because everything outside the circuit runs at normal speed.

The second constraint is the field. Mina works over a finite field, and arithmetic in a circuit is field arithmetic, so the number types you can use inside a circuit are not the JavaScript numbers you are used to. The o1js-pack package exists precisely because of this, packing extra data into a single Field, and the eddsa-o1js package exists because curve operations over that field are not free.

None of this is a criticism of the framework. It is the inherent cost of proving computation, and the framework's job is to hide the cryptography, not the cost. What a reader should take away is that o1js is a TypeScript interface to a proving system with a substantial runtime, and the projects that succeed with it are the ones that keep the proving surface small and push the rest into normal code.

Editorial conclusion

Adopt o1js if you are building a zkApp for the Mina Protocol and want to write circuits in TypeScript rather than in a domain specific proving language, and if you want the browser as a first class proving target. Do not adopt it as a general zero knowledge toolkit, because it is tied to Mina's field, proving system and verifier, and the compile and prove cycle is slow enough that it shapes how you work. Before you commit, scaffold a project with the zkApp CLI, run the project locally with the in-browser or node runner, and install a static analyser such as o1js-scan, because a circuit that does not bind a witness is the failure mode this ecosystem has been bitten by.

Frequently asked questions

What is o1js?

o1js is a TypeScript framework for zk-SNARKs and zkApps. It lets developers write zk circuits and prove plus verify them in a browser or node environment, or create proofs to be verified by the Mina Protocol.

How do I start a new o1js project?

With the zkApp CLI, a separate scaffolding tool published on npm as zkapp-cli, which creates a new project using o1js. The o1js documentation at docs.o1labs.org covers the language features, and the Mina zkApp documentation covers building applications on Mina.

Does o1js work in a browser and in Node?

Yes, and the package has separate entry points for each. The exports map resolves the browser condition to a web build, and the node condition to a node build with both an import and a require path. The prover runs as WebAssembly for the browser target, which is why the build scripts include wasm targets for web and node.

What community packages are available for o1js?

The readme lists o1js-elgamal for partially homomorphic encryption, o1js-pack for packing extra data into a single Field, zk-regex-o1js for compiling ZK Regex circuits, eddsa-o1js for provable EdDSA and twisted Edwards curve operations, and o1js-scan, a static analyser that flags prover-controlled witnesses a circuit never binds.

What is the o1js-scan static analyser for?

It finds zk circuit soundness bugs in o1js zkApps and in Noir circuits, specifically flagging prover-controlled witnesses that the circuit never binds, which is the case where a proof verifies without the output being constrained by the private input. The tool also covers Noir, not just o1js.

Official sources

  1. License: Apache-2.0
  2. o1-labs/o1js on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/o1-labs-o1js.svg)](https://hysenlabs.com/projects/o1-labs-o1js)

Was this useful?