Inspiration

Zero-knowledge proofs are the most powerful idea crypto has, and almost nobody ships them, because shipping them is brutal. Learn Compact, hand-write a circuit, stand up a proof provider and a wallet connector, then fight a WASM runtime through a bundler that was never built to carry it, weeks before a single proof runs. So real products fake it: a checkbox nobody verifies, a login that trusts the client. We didn't want to make zero-knowledge easier to learn. We wanted to make learning it optional.

What it does

Point the CLI at a real app and a predicate, and it finds the trust-the-client shortcut and rewrites the app around a real proof, compiled circuit included, before touching a single file:

npx @midzap/cli add credential-check ./my-app --dry-run

Three predicates ship today, each a drop-in React component backed by a real compiled Compact circuit:

<ProveThreshold field="age" threshold={21} getPrivateValue={() => user.birthYear}>
  <button>Unlock</button>
</ProveThreshold>

Threshold checks, private-set membership, credential validity. Same component either way, swap one prop and it runs against a fast local backend for dev or live Midnight for the real thing. midnightzap doctor re-audits an already-converted app whenever you need proof it's still wired right.

How we built it

Two packages, one seam between them. @midzap/sdk is the runtime, framework-agnostic core plus a React layer, sitting behind a single ProofBackend interface so switching from local preview to live proofs never touches application code. @midzap/cli is the codemod, zero runtime dependencies on purpose, rendering every change through an LCS diff engine we wrote so --dry-run shows a real hunked diff instead of a wall of rewritten source. Every recipe carries its circuit pre-compiled and checked in:

npm publish --workspace=@midzap/sdk
npm publish --workspace=@midzap/cli

Getting a proof to run in an actual browser was the real fight, and it wasn't cryptography, it was the bundler. The Midnight WASM runtime assumes a build environment Vite doesn't hand you for free, so we wired in a WASM plugin, polyfilled the node built-ins it expects, shimmed a websocket import, and switched the build target for native top-level await. Then we made the CLI write that exact config into every app it converts, and proved it by building all four example apps with the full stack live.

We didn't trust our own codemod on faith. Every npm run verify actually runs the CLI against a real bundled app, typechecks the output, builds it, restores the original, and runs unit tests against the recipe logic itself.

Challenges we ran into

Shipping real npm packages surfaced problems a green build never would: a files array quietly dropping the license, publish access defaulting to restricted, a repository URL pointing at a placeholder that didn't exist. Caught all three with a dry-run before anything went live. Then our first choice of scope wasn't ours, so @midnightzap became @midzap everywhere, source, build config, generated output, docs, a full rename we treated as a real regression risk and reverified end to end before trusting it again.

The WASM bundling problem was the one that could've quietly stayed "almost working" forever, a tooling problem three layers down wearing a code problem's clothes. That's the exact gap between a demo that only survives in a sandbox and one that survives in someone else's app.

What we learned

The hard part was never the math, it was the last mile: packaging correctly, making the build tooling work in someone else's project, making a diff trustworthy enough to run against code you actually care about. And saying precisely where the remaining gap is instead of rounding it up: the runtime, the build, and the live backend are real and working right now. What's left is operational, not engineering, a funded wallet and a deployed contract address.

What's next for MidnightZap

Close that last operational gap: deploy the circuits, fund a wallet, run the same demo against live proofs end to end. Then the anonymous upgrade to membership and credential checks, which currently ship a pseudonymous v1, and more recipes, since the codemod doesn't care which trust-the-client shortcut it's replacing.

Built With

Share this project:

Updates

Submission history