Inspiration

AI on sensitive data hits a regulatory triple-bind. The EU AI Act (high-risk enforcement begins August 2026) mandates per-inference audit logs that an independent party can verify. GDPR / HIPAA / CCPA forbid those logs from retaining PII. And US courts only treat digital records as self-authenticating evidence when they're cryptographically signed (Federal Rules of Evidence 902(14)).

Today's logging satisfies none of these. Vendor-attested logs don't meet the EU AI Act's independent-verifiability bar. Plain-text storage breaks GDPR. Centralised logs require forensic experts to admit in court. Yet AI keeps shipping into compliance-critical workflows — robo-advisors, medical triage, customer support touching PII.

WITNESS is the protocol layer that closes the gap.

What it does

WITNESS turns every AI inference into a zero-knowledge receipt: cryptographic proof that the output satisfied a user-defined policy, with content kept encrypted until the user chooses to reveal it.

  1. Sign once, in your wallet. Users register output policies as Compact circuits — "AI must keep risk_score ≤ 50", "AI must not say bitcoin or leverage". Commitments live on Midnight; thresholds and keywords stay private.
  2. Every AI call mints receipts. The SDK extracts deterministic features from the response, generates ZK proofs that features satisfy each active policy, encrypts the request + response with a fresh AES-256-GCM key, and posts one receipt per passing policy to Midnight.
  3. Selective disclosure on demand. Owner clicks Reveal on a receipt. Auditor opens /verify#<token> — no wallet needed. Three badges render: receipt exists on Midnight, SHA-256(plaintext) matches the on-chain commit, owner revealed on-chain (or amber if off-chain link only). The AES key travels in the URL fragment and never reaches a server.

How we built it

  • Compact contract (Compact 0.23, compactc 0.31) — three exported circuits: register_policy, submit_receipt, reveal_receipt. reveal_receipt is owner-only — checks the policy's stored owner pk against the caller's derived pk.
  • Policy library — two primitives live: NumericThreshold and KeywordAbsence, both running through the same submit_receipt circuit. The KeywordAbsence salt is SHA-256("witness:keywords:v1" || JSON(sorted_keywords)) so the on-chain policy_id is cryptographically bound to the keyword set.
  • Client SDK — extractor registry pattern; each primitive ships a deterministic feature extractor; chatMulti(prompt, {policyIds}) runs one LLM call and mints N receipts sequentially.
  • UI — Next.js 16 + React 19, six pages. AES-256-GCM via Web Crypto. Keys persisted in IndexedDB; receipts cached in localStorage.
  • Reveal flow — URL fragment encodes {r, c, k, i, x} (receiptId, contract address, AES key, IV, ciphertext). Base64url. Never reaches the server.

Challenges we ran into

  • Multi-policy nonce race. Minting N receipts increments nonce_counter N times. We made the SDK await sequentially to avoid the race.
  • Next.js 16 Turbopack vs midnight-js ESM. Production build fails on Turbopack; switched to next build --webpack.
  • Vercel framework detection. Monorepo root package.json didn't declare next; Vercel refused to recognise the framework. Added next as a root devDep purely for the detector.
  • Reveal-key transport. AES key for decryption cannot touch the server. We landed on URL fragment (#) which browsers never send in HTTP requests — auditor decrypts entirely client-side.

Accomplishments that we're proud of

  • A single, composable on-chain primitive (submit_receipt) that already supports two distinct policy primitives — and architecturally accepts more without recompiling the contract.
  • Selective-disclosure verification that works for any visitor — no wallet, no login, no API key.
  • 27 unit tests + 3 e2e tests against a live local devnet round-trip; SDK + UI typecheck clean at submission.
  • Apache 2.0, public from day one.

What we learned

  • Compact's disclose() semantics make selective-disclosure protocols feel natural; harder primitives (custom merkle proofs, set membership) become tractable.
  • ZK-ML for policy compliance isn't ready for production (15+ min proving for tiny classifiers). Deterministic feature extractors covering 80% of compliance use cases get the practical wins now.
  • URL fragments are an underused primitive for "secret-that-server-must-not-see" flows.

What's next for WITNESS

  • Hosted proof server so the live demo can run chat end-to-end without a local Docker stack.
  • ScopeMembership policy primitive — third extractor in the registry, topic-classifier-driven, for domain-restricted assistants.
  • Revocable reveal links — re-encrypt under a fresh key, post a superseding receipt, invalidate the old commit on-chain.
  • On-chain policy enumeration so the UI no longer relies on localStorage to remember registered policies. ## What it does

How we built it

Challenges we ran into

Accomplishments that we're proud of

What we learned

What's next for WITNESS

Built With

  • compact
  • midnight-network
  • nextjs
  • openrouter
  • react
  • tailwindcss
  • typescript
  • vercel
  • web-crypto
  • zero-knowledge
Share this project:

Updates