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:
- Zero-Knowledge Proofs: Prove you're authorized to report without revealing who you are
- Rate-Limit Nullifiers (RLN): Prevent spam while maintaining complete anonymity
- 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
- 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
- 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
- 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
- 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
- Shows aggregate numbers to build public trust
- "147 reports submitted, 89% reviewed"
- No identifying information, just accountability metrics
โ๏ธ Settings: Your control center
-
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
- 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
- ๐ GitHub Repository: https://github.com/depapp/midnight-whistleblower
- ๐ Live Demo: https://midnight-whistleblower.vercel.app
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)โ
โโโโโโโโโโโโโโโโโโ
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)
}
๐ฏ 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);
๐ 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
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
๐ญ Playing Both Roles (Tutorial Mode)
Act 1: Become the Moderator
- Navigate to
/moderator
- Click "Generate New Key Pair"
- Copy your public key (this is what reporters need)
- Optional: Export your keys with a password for backup
Act 2: Submit a Report
- Open a new incognito window (for the full experience)
- Go to
/submit
- Paste the moderator's public key
- Write your report: "The coffee machine is plotting against us"
- Attach evidence (optional): photo_of_suspicious_coffee_machine.jpg
- Click Submit and watch the magic:
- Console shows:
[MidnightJS] Generating proof...
- Progress bar indicates encryption
- You get a transaction hash (mock)
- Console shows:
Act 3: Moderate the Report
- Return to the moderator dashboard
- See your encrypted report (looks like gibberish)
- Click to decrypt (proof is verified first)
- Read the shocking coffee machine revelation
- 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!
In your deployed app:
- Go to Settings
- Toggle "Cloud Sync" ON
- 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
Circuit Customization:
// Modify circuits/membership_rln.compact
// Recompile with: npm run compile-circuits
// New artifacts are auto-loaded!
๐ 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)