Files
pwap/backend
SatoshiQaziMuhammed b6c2ce3470 Node 24, and stop pointing at the mobile app that left the repository (#53)
mobile/ was moved out in April (18d41743), but the root package.json still
ran it: `npm run build`, `lint`, `test` and `dev` all called into a missing
directory and failed. The scripts now cover what is here (web, sign,
backend), concurrently is gone with the only command that needed it, and the
root lockfile shrinks to nothing.

The README described the mobile app and not the signing portal; it now lists
the real layout (web, sign, backend, shared, ops).

Node 20 is past its end of life. CI and both images move to Node 24; the
backend's sqlite3 native module builds and its tests pass on 24.
2026-10-09 07:15:39 +04:00
..

🏛️ KYC Council Backend

Backend simulation of pallet-collective voting for KYC approvals.

📋 Overview

Purpose: Decentralized KYC approval system without runtime changes

Architecture:

  • Backend tracks council votes (in-memory)
  • 60% threshold (e.g., 7/11 votes)
  • Auto-executes approve_kyc when threshold reached
  • Uses SUDO account to sign blockchain transactions

🔗 Chain Flow

User applies → Blockchain (PENDING) → Council votes → Backend auto-approves → Welati NFT minted

Why SUDO account?

  • identityKyc.approveKyc() requires EnsureRoot origin
  • Backend signs transactions on behalf of council
  • Alternative: Change runtime to accept council origin (not needed for MVP)

🚀 Installation

1. Install Dependencies

cd /home/mamostehp/pwap/backend
npm install

2. Configure Environment

cp .env.example .env
nano .env

Required variables:

WS_ENDPOINT=wss://ws.pezkuwichain.io
SUDO_SEED=your_sudo_seed_phrase_here
PORT=3001

⚠️ Security Warning: Keep SUDO_SEED secret! Use a dedicated account for KYC approvals only.

3. Start Server

Development (with hot reload):

npm run dev

Production:

npm start

📡 API Endpoints

Council Management

Add Council Member

POST /api/council/add-member
{
  "address": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
  "signature": "0x..." // TODO: Implement signature verification
}

Remove Council Member

POST /api/council/remove-member
{
  "address": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
}

Get Council Members

GET /api/council/members

Response:

{
  "members": ["5DFw...Dwd3", "5Grw...utQY"],
  "totalMembers": 2,
  "threshold": 0.6,
  "votesRequired": 2
}

Sync with Noter Tiki Holders

POST /api/council/sync-notaries

Auto-fetches first 10 Noter tiki holders from blockchain and updates council.


KYC Voting

Propose KYC Approval

POST /api/kyc/propose
{
  "userAddress": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
  "proposerAddress": "5DFwqK698vL4gXHEcanaewnAqhxJ2rjhAogpSTHw3iwGDwd3",
  "signature": "0x..."
}

Logic:

  • Proposer auto-votes AYE
  • If threshold already met (e.g., 1/1 member), auto-executes immediately

Vote on Proposal

POST /api/kyc/vote
{
  "userAddress": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
  "voterAddress": "5DFwqK698vL4gXHEcanaewnAqhxJ2rjhAogpSTHw3iwGDwd3",
  "approve": true,
  "signature": "0x..."
}

Approve: true = AYE, false = NAY

Auto-execute: If votes reach threshold, backend signs and submits approve_kyc transaction.

Get Pending Proposals

GET /api/kyc/pending

Response:

{
  "pending": [
    {
      "userAddress": "5Grw...utQY",
      "proposer": "5DFw...Dwd3",
      "ayes": ["5DFw...Dwd3", "5HpG...vSKr"],
      "nays": [],
      "timestamp": 1700000000000,
      "votesCount": 2,
      "threshold": 7,
      "status": "VOTING"
    }
  ]
}

🔐 Council Membership Rules

Initial Setup

  • 1 member: Founder delegate (hardcoded: 5DFwqK698vL4gXHEcanaewnAqhxJ2rjhAogpSTHw3iwGDwd3)
  • Threshold: 1/1 = 100% (single vote approves)

Growth Path

  1. Add Noter holders: Use /api/council/sync-notaries to fetch first 10 Noter tiki holders
  2. Council size: 11 members (1 founder + 10 notaries)
  3. Threshold: 7/11 = 63.6% (60% threshold met)

Automatic Updates

  • When Noter loses tiki → Remove from council
  • When new Noter available → Add to council (first 10 priority)
  • If no Notaries available → Serok (president) can appoint manually

🧪 Testing

Test 1: Single Member (Founder Only)

# 1. Start backend
npm run dev

# 2. Get council members
curl http://localhost:3001/api/council/members

# Expected: 1 member (founder delegate)

# 3. User applies KYC (via frontend)
# 4. Propose approval
curl -X POST http://localhost:3001/api/kyc/propose \
  -H "Content-Type: application/json" \
  -d '{
    "userAddress": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
    "proposerAddress": "5DFwqK698vL4gXHEcanaewnAqhxJ2rjhAogpSTHw3iwGDwd3"
  }'

# Expected: Auto-execute (1/1 threshold reached)

Test 2: 3 Members

# 1. Add 2 notaries
curl -X POST http://localhost:3001/api/council/add-member \
  -d '{"address": "5HpG...vSKr"}'

curl -X POST http://localhost:3001/api/council/add-member \
  -d '{"address": "5FLe...dXRp"}'

# 2. Council: 3 members, threshold = 2 votes (60% of 3 = 1.8 → ceil = 2)

# 3. Propose
curl -X POST http://localhost:3001/api/kyc/propose \
  -d '{
    "userAddress": "5Grw...utQY",
    "proposerAddress": "5DFw...Dwd3"
  }'

# Status: 1/2 (proposer voted AYE)

# 4. Vote from member 2
curl -X POST http://localhost:3001/api/kyc/vote \
  -d '{
    "userAddress": "5Grw...utQY",
    "voterAddress": "5HpG...vSKr",
    "approve": true
  }'

# Expected: Auto-execute (2/2 threshold reached) ✅

📊 Monitoring

Health Check

GET /health

Response:

{
  "status": "ok",
  "blockchain": "connected",
  "sudoAccount": "5DFw...Dwd3",
  "councilMembers": 11,
  "pendingVotes": 3
}

Console Logs

🔗 Connecting to PezkuwiChain...
✅ Sudo account loaded: 5DFw...Dwd3
✅ Connected to blockchain
📊 Chain: PezkuwiChain
🏛️  Runtime version: 106

🚀 KYC Council Backend running on port 3001
📊 Council members: 1
🎯 Threshold: 60%

📝 KYC proposal created for 5Grw...utQY by 5DFw...Dwd3
📊 Votes: 1/1 (1 members, 60% threshold)
🎉 Threshold reached for 5Grw...utQY! Executing approve_kyc...
📡 Transaction status: Ready
📡 Transaction status: InBlock
✅ KYC APPROVED for 5Grw...utQY
🏛️  User will receive Welati NFT automatically

🔄 Integration with Frontend

Option A: Direct Backend API Calls

Frontend calls backend endpoints directly (simpler for MVP).

// Propose KYC approval
const response = await fetch('http://localhost:3001/api/kyc/propose', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    userAddress: application.address,
    proposerAddress: selectedAccount.address,
    signature: await signMessage(...)
  })
});

Option B: Blockchain Events + Backend Sync

Backend listens to blockchain events and auto-tracks proposals (future enhancement).


🚨 Security Considerations

  1. SUDO Account Protection:

    • Use dedicated hardware wallet (Ledger recommended)
    • Only use for KYC approvals, nothing else
    • Consider multi-sig in production
  2. Signature Verification:

    • TODO: Implement Polkadot signature verification
    • Prevent vote spam from non-members
  3. Rate Limiting:

    • Add rate limiting to API endpoints
    • Prevent DoS attacks
  4. Audit Trail:

    • Log all votes to database (future enhancement)
    • Track council member changes

📝 TODO

  • Implement signature verification
  • Add database persistence (replace in-memory Maps)
  • Add rate limiting middleware
  • Add automated council sync cron job
  • Add multi-sig support for sudo account
  • Add audit logging
  • Add Prometheus metrics
  • Add Docker support

📞 Support

Backend Developer: [Your contact] Runtime Issues: Check /Pezkuwi-SDK/pezkuwi/pallets/identity-kyc Frontend Integration: See /pwap/ADMIN_KYC_GUIDE.md


Version: 1.0 (Backend Council MVP) Last Updated: 17 Kasım 2025