Privacy‑First Job Applications with Midnight Network



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
zk job board

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: screenshot 1

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:

  1. Navigate to “Submit Job”
  2. Create a job posting:
    • Job Title: “Senior Blockchain Developer”
    • Required Skills: Select “Solidity”, “ZK Proofs”, “Rust”
    • Min Experience: 3 years
    • Allowed Regions: NA, EU
  3. Submit and note the Job ID

As an Applicant:

  1. Browse to the job listing
  2. Click “Apply to This Job”
  3. Generate mock credentials:
    • Your skills include the required ones plus others
    • Your experience exceeds the minimum
    • Your region matches
  4. Click “Generate Proof” and watch the ZK magic happen
  5. Submit application
  6. See the “Your Privacy Was Protected” confirmation

Verify Privacy:

  1. Check the Privacy page to understand the system
  2. Try applying twice to the same job (nullifier prevents it)
  3. 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:

  1. Edit circuits/job_eligibility.cmp
  2. Recompile: npm run compile-circuits
  3. 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:

  1. Dual-Mode Operation: Enables both development (mock) and production (real Midnight) workflows
  2. Client-Side Proof Generation: Sensitive data never leaves the user’s device
  3. Nullifier Design: Per-job nullifiers prevent spam while maintaining cross-job unlinkability
  4. 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