DEV Community

Cover image for Anonymous Whistleblower Inbox with Zero-Knowledge Proofs
depa panjie purnama
depa panjie purnama

Posted on

Anonymous Whistleblower Inbox with Zero-Knowledge Proofs

This is a submission for the Midnight Network "Privacy First" Challenge - Protect That Data prompt

What I Built

Midnight Whistleblower is a privacy-first DApp that reimagines secure reporting. Imagine needing to report something sensitive (corruption, safety violations, or misconduct) but fearing retaliation. Traditional systems force you to choose: stay silent or risk exposure. Midnight Whistleblower breaks this dilemma.

The Magic โœจ

Our platform combines three powerful privacy technologies:

  1. Zero-Knowledge Proofs: Prove you're authorized to report without revealing who you are
  2. Rate-Limit Nullifiers (RLN): Prevent spam while maintaining complete anonymity
  3. End-to-End Encryption: Only the designated moderator can decrypt reports, not even our servers can read them

The Journey Through Every Page ๐Ÿ—บ๏ธ

๐Ÿ  Home Page: Your gateway to anonymous truth
home description

  • Clean, minimalist design that doesn't intimidate
  • Two clear paths: "I need to report something" or "I'm a moderator"
  • Privacy promise front and center: "Your identity is mathematically protected"

๐Ÿ“ Submit Report (Reporter Page): Where courage meets cryptography
submit description

  • Step 1: Trust Setup. Paste the moderator's public key (like choosing who gets to read your sealed letter)
  • Step 2: Your Story. Write your report with confidence. Add evidence. Every keystroke stays local until encrypted
  • Step 3: The Magic Moment. Click submit and watch as:
    • Your browser generates a ZK proof (proving you can submit without saying who you are)
    • Content gets encrypted with military-grade AES-GCM
    • A unique nullifier prevents double-submission this epoch
    • You get a mock transaction hash (no real money involved, per challenge rules)

๐Ÿ” Moderator Dashboard: The trusted guardian's toolkit
moderator description

  • Key Vault: Generate, import, or export your cryptographic keys with password protection
  • Encrypted Inbox: See reports as mysterious locked boxes until you decrypt them
  • Proof Verification: Each report's ZK proof is verified before you can decrypt
  • Workflow Management: Mark as reviewed or archive (stats update instantly)
  • The Trust Model: Your private key never leaves your browser. Ever.

๐Ÿ“Š Metrics Page: Understanding the flow of truth
metrics description

  • Real-time counts: pending, reviewed, archived
  • Trend visualization over time
  • Helps moderators manage workload and spot patterns
  • All derived from encrypted data. no content exposure

๐Ÿ“ˆ Public Stats: Transparency without compromise
stats description

  • Shows aggregate numbers to build public trust
  • "147 reports submitted, 89% reviewed"
  • No identifying information, just accountability metrics

โš™๏ธ Settings: Your control center
settings description

  • Cloud Sync Toggle: One switch, no complexity
    • OFF = Pure local mode, your data never leaves your device
    • ON = Encrypted sync via Vercel KV for multi-device access
  • Smart Contract Mode: Enabled by default (mocked for the challenge)
  • Advanced Options: Hidden unless you need them. we respect your cognitive load

๐Ÿ›ก๏ธ Privacy Page: Our promises, explained
privacy description

  • Plain English explanation of our cryptography
  • What we store (ciphertext), what we don't (your identity)
  • How RLN works without a PhD in mathematics
  • Your rights and our commitments

Demo

Architecture Overview

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    USER BROWSER                         โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚   React App + MidnightJS + Web Crypto API       โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                           โ”‚                             โ”‚
โ”‚        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”‚
โ”‚        โ–ผ                  โ–ผ                  โ–ผ          โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”       โ”‚
โ”‚  โ”‚ZK Proofs โ”‚      โ”‚Encryptionโ”‚      โ”‚   RLN    โ”‚       โ”‚
โ”‚  โ”‚(Groth16) โ”‚      โ”‚(AES-GCM) โ”‚      โ”‚Nullifiersโ”‚       โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜       โ”‚
โ”‚                           โ”‚                             โ”‚
โ”‚                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”                      โ”‚
โ”‚                    โ”‚  IndexedDB  โ”‚                      โ”‚
โ”‚                    โ”‚(Local First)โ”‚                      โ”‚
โ”‚                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                            โ”‚ (Optional)
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚  Vercel KV     โ”‚
                    โ”‚(Encrypted Only)โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
Enter fullscreen mode Exit fullscreen mode

How I Used Midnight's Technology

๐Ÿ”ฎ The Compact Circuit (circuits/membership_rln.compact)

// Simplified view of our RLN circuit
circuit MembershipRLN {
    // Public inputs
    merkleRoot: Field
    epoch: Field
    nullifier: Field
    signalHash: Field

    // Private inputs (never revealed!)
    identitySecret: Field
    merklePath: Field[20]

    // The magic: prove membership without revealing identity
    assert(verifyMerkleProof(identitySecret, merklePath) == merkleRoot)
    assert(hash(identitySecret, epoch) == nullifier)
}
Enter fullscreen mode Exit fullscreen mode

๐ŸŽฏ MidnightJS Integration

Our src/lib/midnightjs.ts orchestrates the privacy symphony:

// Load compiled artifacts (proving key, verification key, WASM)
const artifacts = await ArtifactLoader.loadArtifacts('membership_rln');

// Generate proof (happens in reporter's browser)
const proof = await midnightJS.generateProof('membership_rln', {
    merkleRoot: organizationRoot,
    epoch: currentEpoch,
    identitySecret: userSecret,  // Never leaves the browser!
    merklePath: membershipPath
});

// Verify proof (happens in moderator's browser)
const isValid = await midnightJS.verifyProof('membership_rln', proof);
Enter fullscreen mode Exit fullscreen mode

๐Ÿš€ The Development Experience

We built a two-speed system:

  • Production Mode: Real ZK proofs using compiled Midnight artifacts
  • Dev Mode: Lightning-fast stubs for rapid iteration (VITE_USE_REAL_MIDNIGHT=false)

This means you can develop the UX at full speed, then switch to real proofs for testing.

Data Protection as a Core Feature

๐Ÿ” The Privacy Stack

Layer 1: Anonymous Identity

  • ZK membership proofs let you prove "I'm allowed to submit" without revealing who you are
  • Like showing you have a valid ticket without showing your name

Layer 2: Anti-Spam Without Tracking

  • RLN nullifiers prevent multiple submissions per day
  • The clever part: we can detect duplicates without knowing who submitted them
  • Nullifier = Hash(YourSecret + Today'sDate)

Layer 3: End-to-End Encryption

// In the reporter's browser
const encrypted = await encryptMessage(reportContent, moderatorPublicKey);
// Now it's gibberish to everyone except the moderator

// In the moderator's browser (and nowhere else!)
const decrypted = await decryptMessage(encrypted, moderatorPrivateKey);
// The report is revealed, but the identity remains hidden
Enter fullscreen mode Exit fullscreen mode

Layer 4: Local-First Architecture

  • Your data lives in IndexedDB by default
  • Cloud sync is optional and only moves ciphertext
  • You own your data, we just help move it

๐Ÿ›ก๏ธ What Makes This Different

Traditional whistleblower systems:

  • โŒ "Trust us, we'll protect your identity" (trust-based)
  • โŒ "Use Tor and hope for the best" (network-based)
  • โŒ "Create a throwaway email" (weak pseudonymity)

Midnight Whistleblower:

  • โœ… Mathematical proof of anonymity (ZK-based)
  • โœ… Encryption at rest and in transit (cryptography-based)
  • โœ… No correlation possible across submissions (nullifier-based)
  • โœ… Works even if our servers are compromised (client-based)

Set Up Instructions / Tutorial

๐ŸŽฏ Quick Start (5 minutes)

Prerequisites: Node.js 18+, Git

# 1. Clone the repository
git clone https://github.com/depapp/midnight-whistleblower.git
cd midnight-whistleblower

# 2. Install dependencies
npm install

# 3. Compile the Midnight circuits (this is the magic!)
npm run compile-circuits

# 4. Start the development server
npm run dev

# 5. Open http://localhost:5173
Enter fullscreen mode Exit fullscreen mode

๐ŸŽญ Playing Both Roles (Tutorial Mode)

Act 1: Become the Moderator

  1. Navigate to /moderator
  2. Click "Generate New Key Pair"
  3. Copy your public key (this is what reporters need)
  4. Optional: Export your keys with a password for backup

Act 2: Submit a Report

  1. Open a new incognito window (for the full experience)
  2. Go to /submit
  3. Paste the moderator's public key
  4. Write your report: "The coffee machine is plotting against us"
  5. Attach evidence (optional): photo_of_suspicious_coffee_machine.jpg
  6. Click Submit and watch the magic:
    • Console shows: [MidnightJS] Generating proof...
    • Progress bar indicates encryption
    • You get a transaction hash (mock)

Act 3: Moderate the Report

  1. Return to the moderator dashboard
  2. See your encrypted report (looks like gibberish)
  3. Click to decrypt (proof is verified first)
  4. Read the shocking coffee machine revelation
  5. Mark as "Reviewed" and watch the stats update

๐Ÿš€ Production Deployment (10 minutes)

Deploy to Vercel with Cloud Sync:

# 1. Install Vercel CLI
npm i -g vercel

# 2. Deploy (follow prompts)
vercel

# 3. Add KV Storage in Vercel Dashboard
# Project Settings โ†’ Storage โ†’ Create Database โ†’ KV

# 4. Environment variables are auto-configured!
Enter fullscreen mode Exit fullscreen mode

In your deployed app:

  1. Go to Settings
  2. Toggle "Cloud Sync" ON
  3. That's it! Multi-device sync with zero configuration

๐Ÿ”ง Advanced Configuration

Development Modes:

# Fast iteration mode (stub proofs)
VITE_USE_REAL_MIDNIGHT=false npm run dev

# Disable mock contract
VITE_USE_CONTRACT=false npm run dev

# Custom sync endpoint (for self-hosting)
VITE_SYNC_BASE_URL=https://your-api.com npm run dev
Enter fullscreen mode Exit fullscreen mode

Circuit Customization:

// Modify circuits/membership_rln.compact
// Recompile with: npm run compile-circuits
// New artifacts are auto-loaded!
Enter fullscreen mode Exit fullscreen mode

๐Ÿ› Troubleshooting Guide

Problem Solution
"Artifacts not found" Run npm run compile-circuits
"Proof generation failed" Check browser console for [MidnightJS] logs
"Can't decrypt report" Verify you're using the correct private key
"Stats not updating" Wait 500ms for cloud sync, then refresh
"CORS errors in dev" Use VITE_SYNC_BASE_URL for cross-origin

๐Ÿ“š Learning Resources

Understand the Code:

  • src/lib/midnightjs.ts - See how ZK proofs work
  • src/lib/encryption.ts - Learn Web Crypto API patterns
  • src/pages/ReporterPage.tsx - Follow the submission flow
  • ARCHITECTURE.md - Deep dive into system design

The Vision ๐ŸŒŸ

Imagine a world where:

  • Employees can report harassment without fear
  • Citizens can expose corruption safely
  • Students can report bullying anonymously
  • Journalists' sources are mathematically protected

Midnight Whistleblower isn't just a demo, it's a blueprint for privacy-respecting applications that can change the world.

Top comments (0)