This content originally appeared on DEV Community and was authored by depa panjie purnama
This is a submission for the Midnight Network “Privacy First” Challenge – Protect That Data prompt
What I Built
I built ZK Job Board, a privacy-preserving job marketplace where applicants can prove they meet job requirements without revealing their actual personal data. Using Midnight Network’s Compact language and MidnightJS SDK, the application enables:
- Anonymous Eligibility Proofs: Applicants can prove “I have the required skills, experience, and am in an allowed region” without disclosing their exact skills list, years of experience, or specific location
- Employer Confidence: On-chain proof verification ensures claims are cryptographically valid, preventing dishonest applications
- Anti-Spam Protection: Nullifier-based system enforces one application per job per applicant without linking identity across different jobs
This directly addresses real-world hiring privacy concerns where candidates often over-expose credentials during applications, risking data misuse, discrimination, or identity theft.
Demo
GitHub Repository: https://github.com/depapp/zk-job-board
- Screenshot:
Key Components:
-
ZK Circuits:
circuits/job_eligibility.cmp
– Compact language implementation -
UI:
app/src/
– React + TypeScript + Tailwind CSS -
Midnight Integration:
app/src/lib/midnight.ts
– Full SDK integration -
Proof Artifacts:
artifacts/zk/
– Compiled circuit outputs
How I Used Midnight’s Technology
1. Compact Language for Circuit Design
The core privacy logic lives in circuits/job_eligibility.cmp
:
circuit JobEligibility {
// Public inputs (visible to verifier)
public jobId: Field
public policyHash: Field
public nullifier: Field
// Private inputs (never revealed)
private skillsBitset: Field[32]
private experienceYears: Field
private regionIndex: Field
private secret: Field
// Constraints enforce:
// 1. Skills subset check (applicant ⊇ required)
// 2. Experience threshold (years ≥ minimum)
// 3. Region membership (region ∈ allowed)
// 4. Nullifier derivation (prevents duplicates)
}
2. MidnightJS SDK Integration
The app/src/lib/midnight.ts
file orchestrates the entire proof lifecycle:
// Dual-mode design for flexibility
if (VITE_MIDNIGHT_ENABLED === 'true') {
// Real Midnight Network integration
const { httpClientProofProvider } = await import('@midnight-ntwrk/midnight-js-http-client-proof-provider');
const { FetchZkConfigProvider } = await import('@midnight-ntwrk/midnight-js-fetch-zk-config-provider');
// Generate real proofs via SDK
const proof = await proofProvider.prove('job_eligibility', witness, zkConfig);
} else {
// Mock mode for development/demo
return generateMockProof(publicInputs, privateInputs);
}
3. Build & Deployment Scripts
-
Circuit Compilation (
scripts/compile-circuits.ts
):- Compiles Compact code to proving/verification keys
- Outputs artifacts to
artifacts/zk/
-
Verifier Deployment (
scripts/deploy-verifier.ts
):- Deploys on-chain verifier contract
- Persists contract address for app integration
Data Protection as a Core Feature
Privacy isn’t an afterthought — it’s the foundation of every design decision:
What Stays Private:
- Exact Skills: The verifier never sees your full skill list, only that you have the required subset
- Precise Experience: Your exact years remain hidden; only proof of meeting the minimum is revealed
- Specific Location: Your exact region stays private; only membership in allowed regions is proven
- Personal Identity: No PII is ever exposed or stored
What Gets Proven:
“I have all required skills” (without listing other skills)
“I have enough experience” (without revealing exact years)
“I’m in an allowed region” (without specifying which one)
“This is my only application to this job” (via nullifier)
UI Privacy Reinforcement:
Lock icons mark all private data fields
- Clear explanations at each step about what remains hidden
- Dedicated
/privacy
page educating users on ZK benefits - Success page explicitly lists “What Was Protected” vs “What Was Proven”
Set Up Instructions / Tutorial
This comprehensive tutorial is designed for developers of all skill levels and includes both quick-start (mock mode) and production (real Midnight) paths.
Prerequisites
- Node.js 18+ and npm
- Git
- Modern web browser
Step 1: Clone and Install
# Clone the repository
git clone https://github.com/depapp/zk-job-board.git
cd zk-job-board
# Install dependencies
npm install
Step 2: Environment Configuration
Create your local environment file:
cp .env.example .env.local
Option A: Mock Mode (Easiest – No Credentials Needed)
Leave .env.local
as-is or set:
VITE_MIDNIGHT_ENABLED=false
Option B: Real Midnight Integration (Recommended for Full Experience)
# Enable Midnight Network
VITE_MIDNIGHT_ENABLED=true
# Network Configuration
VITE_MIDNIGHT_RPC_URL=https://testnet.midnight.network/rpc
VITE_MIDNIGHT_NETWORK_ID=testnet-02
VITE_MIDNIGHT_API_KEY=your-api-key-here
# Proof Server (local or hosted)
VITE_PROOF_SERVER_URL=http://localhost:6300
# Verifier Address (set after deployment)
VITE_VERIFIER_ADDRESS=
Getting Credentials: Sign up at midnight.network for testnet access
Step 3: Compile the ZK Circuit
npm run compile-circuits
This command:
- Compiles
circuits/job_eligibility.cmp
- Generates proving and verification keys
- Saves artifacts to
artifacts/zk/
Expected output:
[Circuit] Compiling job_eligibility.cmp...
[Circuit] Generating proving key...
[Circuit] Generating verification key...
[Circuit] Artifacts saved to artifacts/zk/
✅ Circuit compilation complete!
Step 4: Deploy the Verifier (Real Mode Only)
npm run deploy-verifier
This will:
- Deploy the verifier contract to Midnight testnet
- Save the contract address to
.env.local
- Enable on-chain verification in the app
Step 5: Start the Application
# Development mode with hot reload
npm run dev
# Visit http://localhost:5173
Step 6: Try the Complete Flow
As an Employer:
- Navigate to “Submit Job”
- Create a job posting:
- Job Title: “Senior Blockchain Developer”
- Required Skills: Select “Solidity”, “ZK Proofs”, “Rust”
- Min Experience: 3 years
- Allowed Regions: NA, EU
- Submit and note the Job ID
As an Applicant:
- Browse to the job listing
- Click “Apply to This Job”
- Generate mock credentials:
- Your skills include the required ones plus others
- Your experience exceeds the minimum
- Your region matches
- Click “Generate Proof” and watch the ZK magic happen
- Submit application
- See the “Your Privacy Was Protected” confirmation
Verify Privacy:
- Check the Privacy page to understand the system
- Try applying twice to the same job (nullifier prevents it)
- Apply to different jobs (nullifiers don’t link your identity)
Step 7: Understanding the Console Logs
The app provides detailed logging for educational purposes:
// Mock mode logs
[Midnight] SDK loading skipped (VITE_MIDNIGHT_ENABLED is not true)
[Midnight] Using mock proof generation
// Real mode logs
[Midnight] Loading SDK modules...
[Midnight] Connected to testnet-02
[Midnight] Using real proof generation via SDK
[Midnight] Proof verified on-chain at 0x...
Step 8: Troubleshooting Guide
Issue | Solution |
---|---|
“SDK modules failed to load” | Ensure npm install completed successfully |
“ZK config not available” | Run npm run compile-circuits
|
“Verifier address not configured” | Run npm run deploy-verifier or use mock mode |
“Connection failed” | Check API key and network connectivity |
Proof generation fails | App auto-falls back to mock mode, check console |
Step 9: Advanced Customization
Adding New Skills:
Edit config/allowlist.skills.json
:
{
"skills": ["YourNewSkill", ...]
}
Modifying Circuit Constraints:
- Edit
circuits/job_eligibility.cmp
- Recompile:
npm run compile-circuits
- Redeploy verifier:
npm run deploy-verifier
Switching Between Modes:
Simply toggle VITE_MIDNIGHT_ENABLED
and restart the dev server.
Step 10: Production Deployment
# Build for production
npm run build
# Preview production build
npm run preview
# Deploy to your hosting service
# The app works with any static hosting (Vercel, Netlify, etc.)
Architecture Deep Dive (Bonus)
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Employer │────▶│ Job Policy │────▶│ On-Chain │
│ UI │ │ Creation │ │ Storage │
└─────────────┘ └──────────────┘ └─────────────┘
│
▼
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Applicant │────▶│ ZK Proof Gen │────▶│ Verifier │
│ UI │ │ (Midnight) │ │ Contract │
└─────────────┘ └──────────────┘ └─────────────┘
Key Design Decisions:
- Dual-Mode Operation: Enables both development (mock) and production (real Midnight) workflows
- Client-Side Proof Generation: Sensitive data never leaves the user’s device
- Nullifier Design: Per-job nullifiers prevent spam while maintaining cross-job unlinkability
- Bitset Encoding: Efficient skill matching using bitwise operations in the circuit
Future Enhancements
- DID Integration: Connect with decentralized identity providers for credential attestation
- Employer Reputation: On-chain employer ratings visible only to verified applicants
- Skill Attestations: Third-party skill verification without revealing the verifier
- Batch Applications: Apply to multiple similar jobs with a single proof
ZK Job Board demonstrates how Midnight Network enables practical privacy-preserving applications that solve real problems. By making privacy the default rather than an option, we can build systems that respect user data while maintaining functionality and trust.
This content originally appeared on DEV Community and was authored by depa panjie purnama