A privacy-first financial identity protocol built on Midnight Network
Prove your income, KYC status, and creditworthiness using zero-knowledge proofs without revealing sensitive documents.
- What This Does
- Quick Start
- Prerequisites
- Installation
- Configuration
- Running the App
- Getting API Keys
- Using the Application
- API Documentation
- Development
- Testing
- Deployment
- Troubleshooting
- Project Structure
- Technology Stack
- Security
- Contributing
- License
Bunty enables users to:
- β Verify identity without exposing personal documents
- β Prove income without sharing bank statements
- β Demonstrate creditworthiness privately
- β Generate verifiable credentials for financial services
- β Submit zero-knowledge proofs to blockchain
Key Features:
- Identity verification via Persona (with Stripe fallback)
- Bank account integration via Plaid
- Zero-knowledge proof generation
- Midnight Network blockchain integration
- Lace wallet support
# 1. Clone and install
git clone https://github.com/your-org/bunty-zkp-platform.git
cd bunty-zkp-platform
npm install
# 2. Set up environment
cp .env.template backend/.env
nano backend/.env # Add your database credentials
# 3. Set up database
createdb bunty
psql -d bunty -f backend/db/init.sql
psql -d bunty -f backend/init-db.sql
# 4. Start the app
npm run dev
# 5. Open browser
# Frontend: http://localhost:3000
# Backend: http://localhost:3001- Node.js 18+ (tested with v22.19.0)
- PostgreSQL 15+ (running on port 5432)
- Redis 7+ (running on port 6379)
- npm or yarn
- Docker (for Midnight Network services)
- Midnight Lace Wallet browser extension
- Persona account (for identity verification)
- Plaid account (for bank integration)
# Ubuntu/Debian
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs
# macOS
brew install node
# Verify
node --version # Should be 18+# Ubuntu/Debian
sudo apt install -y postgresql postgresql-contrib
# macOS
brew install postgresql@15
# Start PostgreSQL
sudo systemctl start postgresql # Linux
brew services start postgresql@15 # macOS# Ubuntu/Debian
sudo apt install -y redis-server
# macOS
brew install redis
# Start Redis
sudo systemctl start redis # Linux
brew services start redis # macOSgit clone https://github.com/your-org/bunty-zkp-platform.git
cd bunty-zkp-platform
npm install# Create database and user
sudo -u postgres psql << EOF
CREATE DATABASE bunty;
CREATE USER bunty_user WITH PASSWORD 'bunty_password';
GRANT ALL PRIVILEGES ON DATABASE bunty TO bunty_user;
\c bunty
GRANT ALL ON SCHEMA public TO bunty_user;
EOF
# Run schema
psql -U bunty_user -d bunty -f backend/db/init.sql
psql -U bunty_user -d bunty -f backend/init-db.sql# Copy template
cp .env.template backend/.env
# Edit with your values
nano backend/.envMinimum Required Configuration:
# Database (Required)
DATABASE_URL=postgresql://bunty_user:bunty_password@localhost:5432/bunty
# Redis (Required)
REDIS_URL=redis://localhost:6379
# JWT Secrets (Required - use random strings)
JWT_SECRET=your-super-secret-jwt-key-min-32-chars
JWT_REFRESH_SECRET=your-super-secret-refresh-key-min-32-chars
# Server (Required)
PORT=3001
NODE_ENV=development
CORS_ORIGIN=http://localhost:3000
# Identity Provider (Choose one)
USE_PERSONA=false # Set to true when you have Persona keysGenerate Secure Secrets:
# Generate JWT secrets
openssl rand -base64 32 # For JWT_SECRET
openssl rand -base64 32 # For JWT_REFRESH_SECRET# Start both frontend and backend
npm run dev
# Or start separately
cd backend && npm run dev # Backend only
cd frontend && npm run dev # Frontend only- Frontend: http://localhost:3000
- Backend API: http://localhost:3001
- Health Check: http://localhost:3001/health
# Check backend health
curl http://localhost:3001/health
# Expected: {"status":"ok","timestamp":"..."}
# Check database connection
psql -U bunty_user -d bunty -c "SELECT 1;"
# Expected: 1 row returned
# Check Redis
redis-cli ping
# Expected: PONG- Sign up: https://withpersona.com
- Get API Key:
- Go to Dashboard β API Keys
- Create Sandbox API key
- Copy to
PERSONA_API_KEY
- Create Template:
- Go to Templates β Create verification template
- Copy template ID to
PERSONA_TEMPLATE_ID
- Set up Webhook:
- Go to Webhooks β Add endpoint
- URL:
http://localhost:3001/api/identity/webhook - Events:
inquiry.completed,inquiry.failed,inquiry.expired - Copy secret to
PERSONA_WEBHOOK_SECRET
- Update .env:
USE_PERSONA=true PERSONA_API_KEY=persona_sandbox_your_key PERSONA_ENVIRONMENT=sandbox PERSONA_TEMPLATE_ID=itmpl_your_template_id PERSONA_WEBHOOK_SECRET=whsec_your_webhook_secret
- Sign up: https://plaid.com
- Get Credentials:
- Go to Dashboard β Team Settings β Keys
- Copy Client ID to
PLAID_CLIENT_ID - Copy Sandbox Secret to
PLAID_SECRET
- Update .env:
PLAID_CLIENT_ID=your_client_id PLAID_SECRET=your_sandbox_secret PLAID_ENV=sandbox
- Sign up: https://stripe.com
- Get API Keys:
- Go to Dashboard β Developers β API Keys
- Copy Secret key to
STRIPE_SECRET_KEY - Copy Publishable key to
STRIPE_PUBLISHABLE_KEY
- Set up Webhook:
- Go to Webhooks β Add endpoint
- URL:
http://localhost:3001/api/identity/webhook - Copy secret to
STRIPE_WEBHOOK_SECRET
- Update .env:
USE_PERSONA=false STRIPE_SECRET_KEY=sk_test_your_key STRIPE_PUBLISHABLE_KEY=pk_test_your_key STRIPE_WEBHOOK_SECRET=whsec_your_secret
1. Open http://localhost:3000
2. Click "Register"
3. Enter email and password
4. Click "Login" with your credentials
1. Navigate to "Verification" page
2. Click "Start Identity Verification"
3. Persona SDK will load
4. Upload government ID
5. Take selfie
6. Enter personal information
7. Submit verification
8. Wait for approval (instant in sandbox)
1. Go to "Financial Data" page
2. Click "Connect Bank Account"
3. Plaid Link will open
4. Select your bank (use "Sandbox" for testing)
5. Login with test credentials
6. Select accounts to link
7. Accounts will appear in dashboard
1. Go to "Witness Generation" page
2. Select proof type:
- Income Proof
- Asset Proof
- Creditworthiness Proof
3. Set threshold (e.g., prove income > $50,000)
4. Click "Generate Witness"
5. Witness hash is created
1. Go to "Proof Generation" page
2. Select witness
3. Click "Generate Proof"
4. Proof server creates zero-knowledge proof
5. Proof is ready for submission
1. Install Midnight Lace Wallet extension
2. Connect wallet to application
3. Go to "Submit Proof" page
4. Review proof details
5. Sign transaction with Lace wallet
6. Proof is submitted to Midnight Network
POST /api/auth/register
Content-Type: application/json
{
"email": "user@example.com",
"password": "SecurePassword123!"
}
Response: {
"message": "User registered successfully",
"userId": "uuid"
}POST /api/auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "SecurePassword123!"
}
Response: {
"token": "jwt_token",
"refreshToken": "refresh_token",
"user": { "id": "uuid", "email": "user@example.com" }
}POST /api/auth/refresh
Content-Type: application/json
{
"refreshToken": "refresh_token"
}
Response: {
"token": "new_jwt_token"
}POST /api/identity/verify
Authorization: Bearer jwt_token
Response: {
"inquiryId": "inq_...",
"sessionToken": "...",
"url": "https://withpersona.com/verify/..."
}GET /api/identity/status
Authorization: Bearer jwt_token
Response: {
"status": "completed",
"verified": true,
"inquiryId": "inq_...",
"verifiedAt": "2024-11-17T12:00:00Z"
}POST /api/identity/webhook
Persona-Signature: signature_from_persona
Content-Type: application/json
{
"type": "inquiry.completed",
"data": {
"id": "inq_...",
"attributes": { ... }
}
}POST /api/plaid/create-link-token
Authorization: Bearer jwt_token
Response: {
"linkToken": "link-sandbox-..."
}POST /api/plaid/exchange-token
Authorization: Bearer jwt_token
Content-Type: application/json
{
"publicToken": "public-sandbox-..."
}
Response: {
"accessToken": "access-sandbox-...",
"itemId": "item_..."
}GET /api/plaid/accounts
Authorization: Bearer jwt_token
Response: {
"accounts": [
{
"id": "account_id",
"name": "Checking",
"type": "depository",
"subtype": "checking",
"balance": 1000.00
}
]
}GET /api/plaid/transactions?startDate=2024-01-01&endDate=2024-12-31
Authorization: Bearer jwt_token
Response: {
"transactions": [
{
"id": "tx_id",
"date": "2024-11-17",
"amount": 50.00,
"name": "Coffee Shop",
"category": ["Food and Drink"]
}
]
}POST /api/witness/generate
Authorization: Bearer jwt_token
Content-Type: application/json
{
"type": "income",
"threshold": 50000
}
Response: {
"witnessHash": "0x...",
"witness": {
"userId": "uuid",
"personaVerified": true,
"ssnVerified": true,
"income": 75000,
"threshold": 50000
}
}GET /api/witness/:hash
Authorization: Bearer jwt_token
Response: {
"witnessHash": "0x...",
"witness": { ... },
"createdAt": "2024-11-17T12:00:00Z"
}POST /api/proof/generate
Authorization: Bearer jwt_token
Content-Type: application/json
{
"witnessHash": "0x...",
"circuit": "verifyIncome"
}
Response: {
"proof": "0x...",
"publicInputs": {
"threshold": 50000
},
"publicOutputs": {
"meetsThreshold": true
}
}POST /api/proof/submit
Authorization: Bearer jwt_token
Content-Type: application/json
{
"proof": "0x...",
"signedTx": "0x..."
}
Response: {
"txHash": "0x...",
"status": "pending",
"submissionId": "uuid"
}GET /api/proof/status/:submissionId
Authorization: Bearer jwt_token
Response: {
"status": "confirmed",
"txHash": "0x...",
"blockNumber": 12345,
"confirmedAt": "2024-11-17T12:00:00Z"
}GET /health
Response: {
"status": "ok",
"timestamp": "2024-11-17T12:00:00Z"
}GET /api/metrics
Authorization: Bearer jwt_token
Response: {
"requests": {
"total": 1000,
"success": 950,
"error": 50
},
"responseTime": {
"avg": 150,
"p95": 300,
"p99": 500
},
"database": {
"connections": 5,
"queries": 5000
}
}bunty-zkp-platform/
βββ frontend/ # Next.js 14 React app
β βββ src/
β β βββ app/ # Pages (App Router)
β β βββ components/ # React components
β β βββ contexts/ # React contexts
β β βββ services/ # API clients
β β βββ types/ # TypeScript types
β βββ public/ # Static assets
βββ backend/ # Express.js API
β βββ src/
β β βββ controllers/ # Route handlers
β β βββ services/ # Business logic
β β βββ middleware/ # Express middleware
β β βββ routes/ # API routes
β β βββ types/ # TypeScript types
β β βββ config/ # Configuration
β β βββ utils/ # Utilities
β β βββ scripts/ # Utility scripts
β βββ db/
β βββ init.sql # Database schema
β βββ migrations/ # Migrations
βββ midnight-contract/ # ZK circuits & contracts
βββ package.json # Monorepo config
# Root level
npm run dev # Start all workspaces
npm run build # Build all workspaces
npm run lint # Lint all workspaces
npm run type-check # Type check all workspaces
# Backend
cd backend
npm run dev # Start with hot reload
npm run build # Build TypeScript
npm test # Run tests
# Frontend
cd frontend
npm run dev # Start with hot reload
npm run build # Build for production
npm run lint # Lint code-
Backend API Endpoint:
- Add route in
backend/src/routes/ - Add controller in
backend/src/controllers/ - Add service logic in
backend/src/services/ - Add types in
backend/src/types/
- Add route in
-
Frontend Page:
- Add page in
frontend/src/app/ - Add components in
frontend/src/components/ - Add API service in
frontend/src/services/ - Add types in
frontend/src/types/
- Add page in
-
Database Changes:
- Create migration in
backend/db/migrations/ - Run migration:
psql -U bunty_user -d bunty -f migration.sql
- Create migration in
cd backend
# Test Persona integration
npx tsx src/scripts/test-persona-integration.ts
# Test witness generation
npx tsx src/scripts/test-witness-persona.ts
# Test feature flags
npx tsx src/scripts/test-feature-flags.ts
# Test database migration
npx tsx src/scripts/test-migration.tscd frontend
npm test# Full integration test
npx tsx backend/src/scripts/test-witness-persona-integration.ts- 2+ CPU cores
- 4GB+ RAM
- 20GB+ storage
- Ubuntu 20.04+ or similar
# Update system
sudo apt update && sudo apt upgrade -y
# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs
# Install PostgreSQL
sudo apt install -y postgresql postgresql-contrib
# Install Redis
sudo apt install -y redis-server
# Install Nginx
sudo apt install -y nginx
# Install PM2
sudo npm install -g pm2# Create production database
sudo -u postgres psql << EOF
CREATE DATABASE bunty_production;
CREATE USER bunty_prod WITH PASSWORD 'secure_password';
GRANT ALL PRIVILEGES ON DATABASE bunty_production TO bunty_prod;
\c bunty_production
GRANT ALL ON SCHEMA public TO bunty_prod;
EOF
# Run migrations
psql -U bunty_prod -d bunty_production -f backend/db/init.sql
psql -U bunty_prod -d bunty_production -f backend/init-db.sql# Clone repository
git clone https://github.com/your-org/bunty-zkp-platform.git
cd bunty-zkp-platform
# Install dependencies
npm install
# Build backend
cd backend
npm run build
# Configure environment
cp .env.example .env
nano .env # Add production values
# Start with PM2
pm2 start dist/index.js --name bunty-backend
pm2 save
pm2 startupOption A: Vercel (Recommended)
cd frontend
npm install -g vercel
vercel --prodOption B: Self-hosted
cd frontend
npm run build
# Deploy .next folder to your web server# /etc/nginx/sites-available/bunty
server {
listen 80;
server_name api.yourdomain.com;
location / {
proxy_pass http://localhost:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}# Enable site
sudo ln -s /etc/nginx/sites-available/bunty /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx# Install Certbot
sudo apt install -y certbot python3-certbot-nginx
# Get certificate
sudo certbot --nginx -d yourdomain.com -d api.yourdomain.comUpdate webhook URLs in:
- Persona:
https://api.yourdomain.com/api/identity/webhook - Plaid:
https://api.yourdomain.com/api/plaid/webhook
# PM2 status
pm2 status
# View logs
pm2 logs bunty-backend
# Monitor resources
pm2 monit
# Health check
curl https://api.yourdomain.com/health# Database backup
pg_dump -U bunty_prod bunty_production > backup_$(date +%Y%m%d).sql
# Automated daily backup
echo "0 2 * * * pg_dump -U bunty_prod bunty_production > /var/backups/bunty/backup_\$(date +\%Y\%m\%d).sql" | crontab -# Quick rollback (feature flag)
ssh production-server
cd /path/to/bunty-zkp-platform/backend
sed -i 's/USE_PERSONA=true/USE_PERSONA=false/' .env
pm2 restart bunty-backend
# Full rollback
git checkout previous-stable-tag
npm install
cd backend && npm run build
pm2 restart bunty-backend# Check logs
tail -f backend/logs/app.log
# Check database connection
psql -U bunty_user -d bunty -c "SELECT 1;"
# Check Redis connection
redis-cli ping
# Check environment variables
cd backend && cat .env
# Check port availability
lsof -i :3001# Verify backend is running
curl http://localhost:3001/health
# Check CORS settings
grep CORS_ORIGIN backend/.env
# Check browser console (F12)# Check PostgreSQL is running
sudo systemctl status postgresql
# Check database exists
psql -U postgres -l | grep bunty
# Reset database
psql -U postgres -c "DROP DATABASE bunty;"
psql -U postgres -c "CREATE DATABASE bunty;"
psql -U bunty_user -d bunty -f backend/db/init.sql# Test API key
curl https://withpersona.com/api/v1/inquiries \
-H "Authorization: Bearer $PERSONA_API_KEY" \
-H "Persona-Version: 2023-01-05"
# Check webhook configuration
# Go to Persona Dashboard β Webhooks
# Verify endpoint and secret# Test credentials
curl https://sandbox.plaid.com/link/token/create \
-H "Content-Type: application/json" \
-d '{
"client_id": "'$PLAID_CLIENT_ID'",
"secret": "'$PLAID_SECRET'",
"user": {"client_user_id": "test"},
"client_name": "Bunty",
"products": ["auth"],
"country_codes": ["US"],
"language": "en"
}'# Find process using port
lsof -i :3001
# Kill process
kill -9 <PID>
# Or use different port
PORT=3002 npm run dev- Framework: Next.js 14 (App Router)
- Language: TypeScript
- UI: React 18
- Styling: CSS Modules
- State: React Context
- API Client: Axios
- Framework: Express.js
- Language: TypeScript
- Database: PostgreSQL 15+
- Cache: Redis 7+
- Auth: JWT
- Process Manager: PM2
- Network: Midnight Network (Cardano)
- Wallet: Lace Wallet
- Proofs: BLS12-381 ZK-SNARKs
- Contracts: Compact language
- Identity: Persona / Stripe Identity
- Banking: Plaid
- Payments: Sila (optional)
- JWT authentication with refresh tokens
- Rate limiting (per-IP and per-user)
- Input sanitization and validation
- SQL injection prevention
- XSS prevention
- CORS whitelist
- Audit logging
- Webhook signature verification
- API key rotation
- Use strong passwords
- Keep secrets in
.env(never commit) - Rotate API keys regularly
- Use HTTPS in production
- Enable firewall
- Regular security updates
- Monitor logs for suspicious activity
We welcome contributions! Here's how:
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Open a Pull Request
- Use TypeScript strict mode
- Follow existing code patterns
- Add comments for complex logic
- Write meaningful commit messages
- Add tests for new features
type(scope): subject
Examples:
feat(auth): add OAuth2 support
fix(persona): handle webhook errors
docs(readme): update installation steps
MIT License - See LICENSE file for details
- Issues: GitHub Issues
- Email: thatspacebiker@gmail.com
- Identity verification (Persona)
- Bank integration (Plaid)
- Witness generation
- Feature flag system
- Midnight Network integration
- ZK proof generation
- Lace wallet integration
- Mobile app
- Multi-language support
Built with β€οΈ for privacy-first financial services
Version 1.0.0 | Last Updated: November 2025