Skip to content
Oumuamua Labs
Hekate ZK Engine menu

Installation

Add Hekate to a Rust project: the open-source crates, the signed prover binary that hekate-prover-sys downloads and verifies, and the platforms it runs on.

Updated installation.md
On this page

Hekate is a set of Rust crates on crates.io plus one native library, the prover. The crates describe programs and verify proofs on every platform they compile for. hekate-prover-sys fetches the signed prover binary for your platform at build time, checks its signatures and links it.

§ Requirements

  • Rust 1.88 or newer, stable, edition 2024.
  • An ARM64 target to prove on: macOS, Linux with glibc, iOS or Android, as listed under Supported targets. The prover has no x86_64 build. The open crates, the verifier among them, also build for x86_64, where field multiplication runs a software fallback.
  • Xcode Command Line Tools on macOS. The build script runs install_name_tool on the prover library.
  • Network access on the first build, which downloads the prover binary. Later builds reuse the cached copy.

§ Add the crates

A program that proves and verifies needs six core crates, the prover shim and rand for the seed:

bash
cargo add hekate-core hekate-crypto hekate-math hekate-program hekate-sdk hekate-verifier
cargo add hekate-prover-sys --features ct
cargo add rand

A service that only verifies proofs stops after the first line, and no native library enters its build.

CrateRole
hekate-mathBinary tower field arithmetic, the fields the proofs work in
hekate-cryptoHash functions, Merkle trees and the Fiat-Shamir transcript
hekate-coreConfiguration, execution traces and proof types
hekate-programThe circuit DSL: columns, constraints, chiplets, buses and program ids
hekate-sdkPreflight diagnostics and the proof wire format
hekate-verifierThe verifier, pure Rust
hekate-prover-sysLinks the prover binary and exposes prove

Chiplet crates add ready-made tables for standard cryptography. Add the ones your program calls:

CrateProves
hekate-gadgetsInteger arithmetic, RAM and ROM, and the modular exponentiation behind RSA
hekate-keccakKeccak-f[1600], with SHA3-256, SHA3-512, SHAKE128 and SHAKE256 host helpers
hekate-sha2The SHA-256 compression function
hekate-aesAES-128 and AES-256 encryption
hekate-rsaRSA-2048 PKCS#1 v1.5 signature verification
hekate-pqcML-KEM key generation, encapsulation and decapsulation, and ML-DSA verification

hekate-scribble is a test tool: it tampers with a valid trace and fails when your constraints miss the change.

§ Choose the prover variant

hekate-prover-sys needs exactly one of two features. With neither or both, the build stops with an error that names them.

FeatureArithmeticUse it for
ctConstant-timeAny witness that holds private data
publicVariable-time tables, fasterPublic data only: the timing of a run leaks the witness

The feature picks the binary: a ct build links the constant-time prover, a public build the variable-time one.

§ Keep the BLAKE3 hash

Every Hekate crate hashes with BLAKE3 by default, and the published prover binaries use BLAKE3 too. Leave the default on.

The sha2 and sha3 features switch DefaultHasher, the hash behind the transcript and the Merkle trees, to SHA-256 or SHA3-256. Cargo turns a feature on for the whole build as soon as one crate asks for it, and SHA3-256 then wins over SHA-256, which wins over BLAKE3. A verifier on another hash than the prover's derives different challenges, and the proof fails to verify.

§ How the prover binary reaches your build

hekate-prover-sys pins one prover binary per variant and target, and its build script links only a file that matches the pin. It looks in three places, in this order:

  1. HEKATE_PROVER_DYLIB_DIR, a directory that holds the binary, for builds without network access. When set, it is the only place searched.
  2. The cache, ~/.cache/hekate-prover-sys/<prover version>/<variant>/<target>/. HEKATE_PROVER_CACHE_DIR moves its root. A cached file with the wrong SHA-256 is deleted and fetched again.
  3. The release server, at the URL pinned in the crate's manifest. The download lands in the cache.

Wherever the file comes from, the script checks it before linking: its SHA-256 against the manifest, then an Ed25519 signature and an ML-DSA-65 signature (FIPS 204) against publisher keys pinned in the crate. Any mismatch fails the build, and a release build has no way around the checks.

The script copies the library into the build directory and links your program to it there, which lets cargo run and cargo test find it with no setup. A program you run on another machine needs the library shipped with it. For iOS and Android apps, hekate-mobile packages the prover into an .xcframework or an .aar.

hekate_prover_sys::version() and build_id() report which binary your program linked.

§ Prove

prove takes a transcript label, the program, its public instance, the private witness, a Config, a fresh seed and an optional cancel token, and returns the proof.

rust
use hekate_core::config::Config;
use hekate_core::proofs::InnerProof;
use hekate_math::Block128;
use hekate_program::{Program, ProgramInstance, ProgramWitness};
use hekate_prover_sys::prove;
use rand::{TryRng, rngs::SysRng};
use std::error::Error;

fn prove_once<P: Program<Block128>>(
    program: &P,
    instance: &ProgramInstance<Block128>,
    witness: &ProgramWitness<Block128>,
    config: &Config,
) -> Result<InnerProof<Block128>, Box<dyn Error>> {
    let mut seed = [0; 32];
    SysRng.try_fill_bytes(&mut seed)?;

    Ok(prove(b"my-program", program, instance, witness, config, seed, None)?)
}

The label keeps your program's transcript apart from every other program's, and the verifier must use the same bytes. The seed blinds the proof: draw it fresh for every proof, because one seed used for two different witnesses discloses them. hekate_sdk::serialize_proof_bytes turns the proof into bytes for the wire, and deserialize_proof reads them back.

To stop a proof early, pass Some(&token) with a CancelToken and call token.request() from another thread. The token is Send and Sync, and the prover stops at its next check of it: prove then returns an error whose code() is Some(ErrorCode::Cancelled).

§ Verify

Verification is pure Rust and needs no prover binary.

rust
use hekate_core::config::Config;
use hekate_core::proofs::InnerProof;
use hekate_crypto::DefaultHasher;
use hekate_crypto::transcript::Transcript;
use hekate_math::Block128;
use hekate_program::{Program, ProgramInstance};
use hekate_verifier::HekateVerifier;

fn accept<P: Program<Block128> + Sync>(
    program_id: &[u8; 32],
    program: &P,
    instance: &ProgramInstance<Block128>,
    proof: &InnerProof<Block128>,
    config: &Config,
) -> bool {
    let mut transcript = Transcript::<DefaultHasher>::new(b"my-program");

    let verdict = HekateVerifier::<Block128, DefaultHasher>::verify(
        program_id,
        program,
        instance,
        proof,
        &mut transcript,
        config,
    );

    matches!(verdict, Ok(true))
}

program_id is the hash of your circuit, from hekate_program::digest::program_id. Compute it once and keep it in the verifier as a constant: a proof made for any other circuit then fails. The label and the Config must match the prover's. Only Ok(true) accepts a proof; Ok(false) and every error reject it.

§ Supported targets

Each prover release ships both variants for each of these targets.

TargetPlatformLibrary
aarch64-apple-darwinmacOS on Apple Silicon.dylib
aarch64-apple-iosiOS.dylib
aarch64-apple-ios-simiOS Simulator on Apple Silicon.dylib
aarch64-unknown-linux-gnuLinux on ARM64, glibc.so
aarch64-linux-androidAndroid on ARM64.so

Prover Releases lists every version with its files and their SHA-256.

§ Next steps