ZK Toolkit for iOS & Android
Compile a Rust prover crate into a signed .xcframework and .aar with a typed Swift / Kotlin surface. Mobile teams consume one await per prove with structured cancellation. Zero ZK terminology leaks across the FFI boundary.
Why it exists
A multi-second prove on the main thread freezes the UI. On a background queue without cooperative cancellation, iOS sends SIGABRT when the app suspends; Android kills the process when the user navigates away. Manual FFI plumbing means every call site reinvents Task hopping, cancel-token wiring, and panic translation.
This toolkit ships that plumbing once, in the wrapper layer, so the call site sees only try await MyProver.prove(inputs:) (Swift) or MyProver.prove(inputs, Dispatchers.Default) (Kotlin). The wrapper is ~30 lines of Swift and one suspending extension in Kotlin, small enough to read in full.
What it does
- True edge-native. Hekate's binary-field prover runs in-process on the device. No remote prover, no upload, no key escrow. Sumcheck + Brakedown PCS, linear-time, bounded memory, no FFTs.
- Native cancellation.
Task.cancel()(Swift) andJob.cancel()(Kotlin) route throughwithTaskCancellationHandler/suspendCancellableCoroutine.invokeOnCancellationto the FFICancelToken. Rust's prover polls the token at instruction boundaries and unwinds withProveError.Cancelled. - Zero FFI boilerplate. UniFFI generates the Swift / Kotlin surface from
#[uniffi::export]annotations on the Rust side. Mobile devs never see C headers, modulemaps, or JNI signatures. The Rust dev's#[uniffi::export]surface is the mobile SDK. - Panic isolation. The Rust
proveentry point wraps the prover incatch_unwind; any panic is mapped toProveError.Panic(message)— a typed error on the mobile side. Noabort()across the FFI boundary.
Pipeline
cargo xtask ios-build cross-compiles the prover crate to aarch64-apple-ios and aarch64-apple-ios-sim, wraps each slice into a framework, rewrites LC_LOAD_DYLIB against @rpath, generates Swift bindings via uniffi-bindgen, assembles two .xcframework bundles (the dev crate and HekateProverCdylib), and codesigns with $HEKATE_IOS_TEAM_ID.
cargo xtask android-build cross-compiles via cargo-ndk, generates Kotlin bindings, injects System.loadLibrary("hekate_prover_cdylib") into the generated Kotlin (anchored to UniFFI's IntegrityCheckingUniffiLib codegen so an upstream bump fails a fixture test), and packs jniLibs/<abi>/*.so into a single .aar via Gradle.
SDKs
- Hekate Swift — async wrapper, SPM install,
try await MyProver.prove(inputs:). - Hekate Kotlin — coroutine wrapper, Gradle install,
MyProver.prove(inputs, Dispatchers.Default).
Trust model
The prover cdylib is fetched at the dev's cargo build time from the Oumuamua CDN, with Ed25519 + ML-DSA-65 signatures verified against pinned publisher keys. Once the cdylib lands inside the dev's .xcframework / .aar, Apple notarization and Play signing cover the entire app bundle; the original publisher signatures do not ride along, by design.