Skip to content

Starter templates

A ZK application typically has three moving parts: a Noir circuit, a prover that runs near the user (server, browser, or mobile), and a verifier that runs wherever the trust boundary sits (your backend, a chain, or both). This page collects starting points and deployment patterns for each.

Use this when verification happens in your backend (HTTP API, gRPC service, job worker).

Skeleton:

Cargo.toml
[package]
name = "my-verifier"
version = "0.1.0"
edition = "2021"
[dependencies]
provekit-common = "=1.0.2"
provekit-verifier = "=1.0.2"
axum = "0.7" # or actix, tonic, etc.
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
anyhow = "1"

Implementation guide: Rust end-to-end shows the verifier setup. Wrap verify() behind your transport layer; refer to Common errors for failure modes to translate into HTTP statuses.

Use this when the user’s device holds the secret and proves on-device, the canonical privacy-preserving pattern.

The previous Verity package skeleton did not pin a v1.0.2-compatible Noir witness generator. See JS / TypeScript compatibility before building this path. General hosting notes:

  • For Next.js, place WASM artifacts under public/ and configure headers for SharedArrayBuffer (Cross-Origin-Opener-Policy: same-origin, Cross-Origin-Embedder-Policy: require-corp).
  • For Vite, copy WASM artifacts via assetsInclude or a plugin and serve with the same headers.

Use this when the user is on iOS or Android and the secret never leaves their device.

PlatformStarting point
iOSSwift compatibility, native FFI and SDK release checks.
AndroidKotlin compatibility, native FFI and SDK release checks.

Plan ahead for memory configuration (pk_configure_memory / Verity.configureMemory) before backend initialization, mobile circuits routinely require file-backed mmap.

Use the bundled HTTP service when you want artifact-driven verification without writing transport glue.

Terminal window
# From the repo root.
cd tooling/verifier-server
cargo run --release

Configure with the VERIFIER_* environment variables documented in tooling/verifier-server/README.md. The service downloads .pkv and r1cs.json from URLs in each request, restrict those URL sources for production.

Use when the verification result needs to settle on-chain.

Terminal window
# 1. Produce the recursive-verifier inputs.
cargo run --release --bin provekit-cli -- generate-gnark-inputs \
circuit.pkp proof.np \
--params recursive/params --r1cs recursive/r1cs.json
# 2. Run the Go/gnark wrapper.
cd recursive-verifier
go run ./cmd/cli ...

Manage Groth16 proving and verifying keys explicitly. Development-time key generation should not be reused in production. The Go module lives in recursive-verifier/, its CLI is the entry point for one-shot wrapping; its server mode is the entry point for batched workflows.

Dockerfile (sketch)
FROM rust:1.85 AS build
WORKDIR /src
COPY . .
RUN cargo build --release -p verifier-server
FROM debian:bookworm-slim
COPY --from=build /src/target/release/verifier-server /usr/local/bin/
EXPOSE 3000
ENV VERIFIER_SEMAPHORE_LIMIT=2 \
VERIFIER_REQUEST_TIMEOUT=60 \
VERIFIER_TIMEOUT_SECONDS=120
ENTRYPOINT ["/usr/local/bin/verifier-server"]

Adjust the VERIFIER_* envs for your circuit profile. The defaults (one concurrent verification, 10 MiB request body) are conservative.

A minimal release-producing job (GitHub Actions, GitLab CI, or equivalent):

- name: Prepare keys
run: cargo run --release --bin provekit-cli -- prepare . --pkp out/app.pkp --pkv out/app.pkv
- name: Generate test proof
run: cargo run --release --bin provekit-cli -- prove --prover out/app.pkp --input fixtures/Prover.toml --out out/proof.np
- name: Verify locally
run: cargo run --release --bin provekit-cli -- verify --verifier out/app.pkv --proof out/proof.np
- name: Record public inputs
run: cargo run --release --bin provekit-cli -- show-inputs --hex out/app.pkv out/proof.np > out/public_inputs.txt
- name: Publish artifacts with provenance
run: sha256sum out/* > out/CHECKSUMS && upload out/

See Generate artifacts for the full CI evidence template.

.pkv is typically shipped with the application that verifies. Options:

  • Embedded in the binary. Smallest deployment footprint; requires a release per circuit version.
  • Loaded from CDN at startup. Decouples circuit upgrades from app releases; requires integrity checks (subresource integrity or signed manifests).
  • Pinned per request. Verifier server pattern: the client supplies the .pkv URL alongside the proof. Restrict URL sources or proxy through trusted storage.

Avoid these shortcuts, they cause production incidents:

  • Reusing .pkp across circuit versions. Keys are bound to the circuit; mixing breaks verification silently.
  • Logging private witnesses or Prover.toml contents. They are secrets by default.
  • Verifying without checking public inputs. Verification proves the proof; your code proves the proof authorizes the action.
  • Shipping main-branch artifacts without a regeneration plan. Format changes are possible between commits.