This is a submission for the Midnight Network "Privacy First" Challenge - Protect That Data prompt
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.
Our platform combines three powerful privacy technologies:
๐ Home Page: Your gateway to anonymous truth
๐ Submit Report (Reporter Page): Where courage meets cryptography
๐ Moderator Dashboard: The trusted guardian's toolkit
๐ Metrics Page: Understanding the flow of truth
๐ Public Stats: Transparency without compromise
โ๏ธ Settings: Your control center
๐ก๏ธ Privacy Page: Our promises, explained
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ USER BROWSER โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ React App + MidnightJS + Web Crypto API โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโ โ
โ โผ โผ โผ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โ
โ โZK Proofs โ โEncryptionโ โ RLN โ โ
โ โ(Groth16) โ โ(AES-GCM) โ โNullifiersโ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโผโโโโโโโ โ
โ โ IndexedDB โ โ
โ โ(Local First)โ โ
โ โโโโโโโโฌโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ (Optional)
โโโโโโโโโผโโโโโโโโโ
โ Vercel KV โ
โ(Encrypted Only)โ
โโโโโโโโโโโโโโโโโโ
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)
}
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);
We built a two-speed system:
VITE_USE_REAL_MIDNIGHT=false
)This means you can develop the UX at full speed, then switch to real proofs for testing.
Layer 1: Anonymous Identity
Layer 2: Anti-Spam Without Tracking
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
Traditional whistleblower systems:
Midnight Whistleblower:
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
Act 1: Become the Moderator
/moderator
Act 2: Submit a Report
/submit
[MidnightJS] Generating proof...
Act 3: Moderate the Report
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:
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!
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 |
Understand the Code:
src/lib/midnightjs.ts
- See how ZK proofs worksrc/lib/encryption.ts
- Learn Web Crypto API patternssrc/pages/ReporterPage.tsx
- Follow the submission flowImagine a world where:
Midnight Whistleblower isn't just a demo, it's a blueprint for privacy-respecting applications that can change the world.