Home Apps Buy My Licenses Docs Pricing Register App Get License

ZK Licensing Developer Guide

ZK Licensing lets you add privacy-preserving license verification to any software using zero-knowledge proofs on Mina Protocol. No license servers, no email addresses, no tracking.

This documentation covers the developer integration. If you're a user looking to buy or verify a license, see Buy a License or Verify.

Quick Start

Get license verification into your app in under 10 minutes.

  1. Register your app on zklicensing.com/register — connect your Mina wallet, set your price, deploy the zkApp. Takes ~5 minutes.
  2. Copy your verification key from the post-deploy screen.
  3. Install the SDK in your project.
  4. Call verifyLicense() at app startup.
bash # Install the SDK npm install @zklicensing/sdk # or pip install zklicensing
typescript import { verifyLicense } from '@zklicensing/sdk'; const result = await verifyLicense({ proofPath: '~/.config/MyApp/proof.json', zkAppAddress: 'B62qm4KpTRs...Xp9N', // from your deploy verificationKey: 'Mh9xKA...3pRz', // from your deploy }); if (!result.valid) { showUpgradePrompt(); return; } if (result.tier === 'pro') { unlockProFeatures(); }

How It Works

The system has three layers:

  • zkApp (Mina smart contract) — holds the authoritative license state on-chain. Exposes methods purchase() and renew().
  • Proof JSON — a ~22 kB file the buyer downloads. Contains a ZK proof attesting "a valid license exists for this app and tier" without revealing the buyer's wallet address.
  • Verification key — a public key you embed in your app. Used to verify the proof offline in milliseconds.
The proof is offline-verifiable. Your app doesn't need network access during the check — only the proof file and the baked-in verification key are needed.

Key Concepts

ZK Proof

A zero-knowledge proof is a cryptographic object that proves a statement is true without revealing the witness that makes it true. In our case: "a valid purchase transaction exists on Mina" is proven without revealing which wallet address made the purchase.

Verification Key

The verification key is derived from your zkApp's circuit. It's a ~500-byte string you hard-code into your application binary. It's safe to distribute publicly — it can only verify proofs, not create them.

zkApp Address

The on-chain address of your deployed licensing contract. Optionally used for chain-based verification as a fallback when the buyer has lost their proof file.

Registering Your App

Go to register.html and complete the 5-step form:

  • Step 1 — Connect wallet. Your Auro wallet becomes the zkApp's admin key. Keep it safe — it's the only way to update pricing or withdraw funds.
  • Step 2 — App info. Name, slug (used in proof files), description, category.
  • Step 3 — Pricing. Your Mina payment address and license tiers. You can add multiple tiers (Free, Pro, Enterprise).
  • Step 4 — Settings. Grace period, refund window, transfer rules, trial mode.
  • Step 5 — Deploy. Signs a Mina transaction that publishes your zkApp contract.
Deployment costs ~1 MINA in transaction fees. Pricing cannot be changed more than once every 30 days to prevent abuse.

SDK Reference

Available for TypeScript/Node.js, Python, and Rust. All SDKs expose the same interface.

verifyLicense(options)

OptionTypeDescription
proofPathstringAbsolute path to the user's proof.json file
proofJsonobjectAlternative to proofPath — pass the parsed JSON directly
zkAppAddressstringYour deployed zkApp's Mina address (B62q…)
verificationKeystringYour circuit's verification key hash
requireTierstring?Optional — reject if tier doesn't match (e.g. "pro")
allowGraceboolean?Accept proofs within the 7-day grace period (default true)
fallbackChainboolean?If proof is missing, query Mina RPC as fallback (requires network)

Return value

FieldTypeDescription
validbooleanWhether the license is currently valid
tierstring"free" | "pro" | your custom tier name
expiresAtnumberUnix timestamp of expiry (null for lifetime/free)
daysRemainingnumberDays until expiry (negative = already expired)
sourcestring"proof" | "chain" — how the license was verified
errorstring?If invalid: "EXPIRED" | "INVALID_PROOF" | "NOT_FOUND"

zkApp Circuit

The zkApp is written in o1js (TypeScript framework for Mina zkApps). The circuit enforces three rules:

  • The purchase transaction was signed by a valid Mina private key.
  • The payment amount equals the tier price at time of purchase.
  • The proof's public output — { tier, expiresAt } — is correctly derived from the on-chain state.
typescript (o1js) // Simplified circuit sketch class LicensingContract extends SmartContract { // On-chain state @state(PublicKey) admin = State<PublicKey>(); @state(UInt64) proPrice = State<UInt64>(); @state(Field) licenseRoot = State<Field>(); // merkle root @method async purchase(tier: Field, payment: UInt64) { // Verify payment matches tier price const price = this.proPrice.getAndRequireEquals(); payment.assertGreaterThanOrEqual(price); // Emit proof: { tier, expiresAt = now + 1 year } const expiry = UInt64.from(Date.now() + 31_536_000_000); emit({ tier, expiresAt: expiry }); } }

Webhooks

Register a webhook URL in your app settings to receive POST notifications for license events. Useful for syncing to your own backend.

EventTrigger
license.createdNew license purchased
license.renewedLicense renewed on-chain
license.expiredLicense passed expiry timestamp
license.refundedRefund processed within window
json (webhook payload) { "event": "license.renewed", "app": "note-vault-pro", "tier": "pro", "expiresAt": 1813536000, "txHash": "8KpR...mN3x", "network": "mina:mainnet" }

Proof JSON Format

The proof file downloaded by buyers has this structure:

json { "proof": "KChiAArBmF...x9mZpQ==", // base64 ZK proof "publicInput": { "tier": "pro", "expires": 1782000000, // unix timestamp "app": "note-vault-pro" }, "verificationKey": "B62qr7...7kHsN", // matches your embed "network": "mina:mainnet", "txHash": "5JuE...Kp9Q" // purchase tx }

FAQ

What happens when Mina is down?

Proof verification is fully offline — it only reads the local proof.json file and runs the verifier against your embedded verification key. Mina network downtime has zero impact on existing license checks.

Can users share their proof.json?

The proof doesn't contain the buyer's wallet address in the public output, so sharing it would give another user a working license. You can mitigate this by enabling device binding in settings, which ties the proof to a machine fingerprint derived inside the ZK circuit.

Can I change my license price?

Yes — call the zkApp's updatePrice() method from your admin wallet. Changes apply to new purchases only. Existing licenses are unaffected. Price updates are rate-limited to once per 30 days.

What's the 2% platform fee?

ZKLicensing deducts 2% of each payment on-chain before sending the remainder to your payment address. This is enforced by the zkApp circuit — we can't change it after deployment. See Pricing for volume discount tiers.

Is the source code open?

The zkApp circuit and the SDK are open source on GitHub. The web frontend is source-available. We encourage developers to audit the circuit before integrating.