Zero-knowledge proofs,in plain Java.
Prove a fact without revealing the data behind it. Define circuits as Java classes, prove them with a pure-Java Groth16 prover, and verify the proof anywhere — in your JVM or on Cardano.
implementation platform('org.zeroj:zeroj-bom-core:0.1.0-pre12')- JAVA 25
- PURE-JAVA PROVER
- GROTH16 · BLS12-381
- CARDANO PLUTUS V3
8f3a1c…e04d192 BYTESOPEN STANDARDS
- Java 25
- Groth16
- BLS12-381
- Plutus V3
- JuLC
- snarkjs
- Poseidon
/ ZERO-KNOWLEDGE, IN ONE PICTURE
Prove the fact.
Keep the data.
A zero-knowledge proof convinces a verifier that a statement about your data is true — and reveals nothing else. Flip the view to see what each side actually learns.
| Input | Value | Visibility |
|---|---|---|
age | 27 | SECRET |
threshold | 18 | PUBLIC |
| Input | Value | Visibility |
|---|---|---|
memberSecret | 0x51c0…9ae4 | SECRET |
merklePath | [20 sibling hashes] | SECRET |
listRoot | 0x1f9c…04b7 | PUBLIC |
nullifier | 0x77ab…e210 | PUBLIC |
| Input | Value | Visibility |
|---|---|---|
balances | [120 450, 80 300, 61 900] ₳ | SECRET |
liabilities | 250 000 ₳ | PUBLIC |
The prover holds every value and runs the circuit to produce a proof.The verifier sees only public inputs and a ~192-byte proof — yet is convinced the statement is true.
/ HOW IT WORKS
From a Java class
to a Cardano validator.
One circuit, four steps, all in Java. Start with a unit test on your laptop; end with a proof verified by Plutus V3.
Write the rule as a Java class.
Mark inputs @Secret or @Public, return a ZkBool, and the annotation processor generates a typed AgeCheckCircuit companion. No new language to learn.
Write circuits with annotations@ZKCircuit(name = "age-check", version = 1)
public class AgeCheck {
@Prove
ZkBool prove(@Secret @UInt(bits = 8) ZkUInt age,
@Public @UInt(bits = 8) ZkUInt threshold) {
return age.gte(threshold); // the statement you prove
}
}Prove it with a pure-Java prover.
Compile to R1CS, compute the witness from typed inputs, and create a Groth16 proof on BLS12-381. No native libraries, no Node.js, no external CLI.
Follow the quickstartvar circuit = AgeCheckCircuit.build();
var r1cs = circuit.compileR1CS(CurveId.BLS12_381);
var inputs = AgeCheckCircuit.inputs().age(27).threshold(18);
BigInteger[] witness = inputs.calculateWitness(circuit, CurveId.BLS12_381);
// Dev-only single-party setup (-Dzeroj.allowInsecureTrustedSetup=true).
// Real deployments import keys from a multi-party ceremony.
BigInteger tau = PowersOfTauBLS381.generate(4).tauScalar();
try (var keys = Groth16Keys.setupInMemory(
r1cs.constraints(), r1cs.numWires(), r1cs.numPublicInputs(), tau)) {
Groth16ProofBLS381 proof = keys.prove(witness, r1cs.constraints());
}Verify in any JVM.
The verifier gets the verification key, the proof and the public inputs. It never sees the age — only that the statement holds.
Verify proofs in JavaBigInteger[] pub = Arrays.copyOfRange(witness, 1, 1 + r1cs.numPublicInputs());
String vkJson = SnarkjsGroth16Json.verificationKeyJson(keys);
String proofJson = SnarkjsGroth16Json.proofJson(proof);
String publicJson = SnarkjsGroth16Json.publicJson(pub);
CircuitId id = AgeCheckCircuit.circuitId();
var envelope = SnarkjsJsonCodec.toEnvelopeFromJson(proofJson, vkJson, publicJson, id);
var material = VerificationMaterial.of(vkJson.getBytes(UTF_8),
ProofSystemId.GROTH16, CurveId.BLS12_381, id);
boolean valid = new Groth16BLS12381PureJavaVerifier()
.verify(envelope, material)
.proofValid(); // trueVerify it on Cardano.
Reusable Plutus V3 validators, written in Java and compiled by JuLC, check the BLS12-381 pairing on-chain. Your validator adds the policy: who may spend, and only once.
Verify your proof on Cardanovar vk = ProverToCardano.compressVk(keys);
var ic = ListPlutusData.of();
vk.ic().forEach(point -> ic.add(new BytesPlutusData(point)));
// The verification key is baked into the script as parameters.
var script = JulcScriptLoader.load(Groth16BLS12381Verifier.class,
new BytesPlutusData(vk.alpha()), new BytesPlutusData(vk.beta()),
new BytesPlutusData(vk.gamma()), new BytesPlutusData(vk.delta()), ic);
// Public inputs go in the datum, the compressed proof in the redeemer.
var p = ProverToCardano.compressProof(proof); // 48 + 96 + 48 bytes/ THE TOOLKIT
Serious cryptography.
Familiar Java.
Circuits are just Java
@ZKCircuit classes with symbolic ZkField, ZkUInt and ZkBool values. The compiler catches mistakes; generated companions give you typed inputs.
Annotations guideA pure-Java Groth16 prover
Setup, prove and verify on BLS12-381 in pure Java — nothing to install beyond a JDK. GraalVM-friendly, with an optional blst backend when you want it.
Prove with Groth16Built for big circuits
Memory-mapped proving keys and a streaming setup keep circuits with millions of constraints on commodity machines.
Performance guideVerify anywhere
A pluggable verifier SPI for your JVM services, and reusable Plutus V3 validators for Cardano.
Verification guidesReal-world gadgets
Poseidon, Merkle membership, comparators — plus in-circuit SHA-512, Blake2b, Ed25519 and CIP-1852 key derivation.
Gadget libraryPlays well with others
Import circom/snarkjs keys and proofs, export snarkjs-compatible JSON, and issue BBS selective-disclosure credentials.
Interop tutorial/ WHAT WILL YOU BUILD?
Privacy that apps
can actually verify.
Every idea below has a design walkthrough, and most have a runnable end-to-end demo on a local Cardano devnet.
Private voting
Prove you may vote and vote once — without revealing who you are.
Proof of reserves
Show reserves cover liabilities without exposing a single balance.
Age & KYC checks
Pass an eligibility check without handing over your documents.
Private NFT ownership
Prove you hold an asset from a collection without doxxing your wallet.
One claim per person
Sybil-resistant airdrops with nullifiers instead of identity leaks.
Selective disclosure
Reveal only the credential attributes a service needs, verified on-chain.
Product passports
Prove compliance of a product without exposing the supply chain.
Account recovery
Prove you control a Cardano key without revealing the seed.
/ YOUR LEARNING PATH
From zero to on-chain,
one step at a time.
No cryptography background needed. If you can write a unit test, you can write a circuit.
- ≈ 15 MIN
Understand the idea
Provers, verifiers, circuits and witnesses — in plain English, with Java analogies.
Learn ZK - ≈ 10 MIN
Make your first proof
A runnable Gradle project: define, prove and verify a secret in pure Java.
Quickstart - ≈ 1 HOUR
Build real statements
Range proofs, private allowlists with nullifiers, and on-chain verification.
Tutorials - ONGOING
Ship responsibly
Replay protection, trusted setup ceremonies and a security checklist for ZK apps.
Security guide
/ AI-READY DOCUMENTATION
Your coding assistant
can read these docs too.
Every page has a Markdown twin. An AI Starter Pack drops straight into CLAUDE.md,AGENTS.md or a Cursor rule, and a circuit API catalog generated from the Java sources keeps agents from inventing methods.
Read https://zeroj.dev/llms.txt and https://zeroj.dev/ai/starter-pack.md. Using ZeroJ (Groth16, BLS12-381), write a @ZKCircuit that proves a secret balance is at least a public threshold, a JUnit test with a valid and an invalid witness, and the code to verify the proof in Java.
/ HONEST STATUS
Experimental —
and upfront about it.
ZeroJ is research software. It is heavily tested — thousands of tests, official vectors, differential checks against independent implementations and end-to-end runs on a Cardano devnet — but it has not been externally audited. Don’t use it to protect real value yet.
Status & maturity- BETAGroth16 on BLS12-381Pure-Java prove & verify — the focus of this release
- BETAOn-chain Groth16JuLC / Plutus V3 — testnets only, not value-bearing
- BETACircuits & gadgetsAnnotations, DSL, standard library
- BETABBS credentialsCFRG draft; prefer the blst provider for issuer keys
- EXPERIMENTALPlonKProver, verifier and validators — no correctness claims
/ YOUR FIRST PROOF STARTS HERE