geocrop-platform./apps/nextgen/egovernancecertificate.md

18 KiB

MASTER SYSTEM ARCHITECTURE PLAN & EXECUTION PROMPT

E-Governance Cryptographic Licensing Engine & Multi-Tenant CA System

Target Workspace: E:\nextgen\next-gen
SuperAdmin Instance: E:\nextgen\next-gen\superadmin (server/ and client/)
First Tenant Instance: E:\nextgen\next-gen\client (React Vite PWA) and E:\nextgen\next-gen\server (Node/Express API Engine)
Document Purpose: Complete, step-by-step technical architectural prompt for an execution agent to implement asymmetric Ed25519 licensing, hybrid dual-driver caching, global access middleware, and anti-tampering clock protection.


EXECUTIVE ARCHITECTURE OVERVIEW

  ┌─────────────────────────────────────────────────────────────────────────┐
  │                    SUPERADMIN ENGINE (CA PORTAL)                        │
  │                  E:\nextgen\next-gen\superadmin                         │
  │  - Holds Ed25519 PRIVATE KEY (SUPERADMIN_PRIVATE_KEY)                   │
  │  - Signs License Tokens: Ed25519 (EdDSA)                                │
  │  - Exposes JWKS Endpoint: /api/v1/auth/jwks                            │
  │  - Stores Tenant Registry in Supabase Cloud (`tenants`, `tenant_licenses`)│
  └───────────────────────────────────┬─────────────────────────────────────┘
                                      │  Cryptographic License Token
                                      │  (Ed25519 Signed JWT)
                                      ▼
  ┌─────────────────────────────────────────────────────────────────────────┐
  │                   FIRST TENANT INSTANCE & DEPLOYMENTS                   │
  │     Client: E:\nextgen\next-gen\client (React Vite PWA + Web Crypto)    │
  │     Server: E:\nextgen\next-gen\server (Node Express Backend API)       │
  │                                                                         │
  │  - Holds Ed25519 PUBLIC KEY (SUPERADMIN_PUBLIC_KEY / VITE_...)         │
  │  - Storage: Local SQLite/Postgres `tenant_licenses`                     │
  │  - Hybrid Dual-Driver Cache: Memory LRU (`lru-cache`) + Redis (`ioredis`)│
  │  - AccessMiddleware: Enforces Read-Only on Expired Licenses (HTTP 402)   │
  │  - Clock Anti-Tamper: Heartbeat sync with SuperAdmin + NTP drift check  │
  └─────────────────────────────────────────────────────────────────────────┘

1. DATABASE SCHEMA & DATA MODELS

1.1 SuperAdmin Cloud Database (Postgres / Supabase)

Target Schema File: E:\nextgen\supabase\supabase_full_schema.sql
Service Integration: E:\nextgen\next-gen\superadmin\server\src\services\SupabaseSuperAdminService.js

-- Full Supabase Schema Updates for Asymmetric Ed25519 Licensing Engine
-- Located in: E:\nextgen\supabase\supabase_full_schema.sql

CREATE TABLE IF NOT EXISTS tenant_licenses (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
  license_key TEXT UNIQUE NOT NULL,
  term_name TEXT NOT NULL,
  academic_year TEXT NOT NULL,
  start_date DATE NOT NULL,
  end_date DATE NOT NULL,
  grace_period_days INTEGER DEFAULT 7,
  max_students INTEGER DEFAULT 1000,
  max_staff INTEGER DEFAULT 200,
  signed_license_token TEXT,
  algorithm TEXT DEFAULT 'EdDSA',
  status TEXT DEFAULT 'active',
  override_reason TEXT,
  issued_by TEXT,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL
);

-- Idempotent Column Additions & Performance Indexing
ALTER TABLE tenant_licenses ADD COLUMN IF NOT EXISTS signed_license_token TEXT;
ALTER TABLE tenant_licenses ADD COLUMN IF NOT EXISTS algorithm TEXT DEFAULT 'EdDSA';

CREATE INDEX IF NOT EXISTS idx_tenant_licenses_active 
ON tenant_licenses(tenant_id) WHERE status = 'active';

CREATE INDEX IF NOT EXISTS idx_tenant_licenses_active ON tenant_licenses(tenant_id) WHERE is_active = TRUE;


### 1.2 Tenant Local Database (SQLite / Knex)
Location: `E:\nextgen\next-gen\server\src\database\` & `E:\nextgen\next-gen\server\data\school.db`

```sql
-- SQLite Compatible Tenant License Table
CREATE TABLE IF NOT EXISTS tenant_licenses (
    id TEXT PRIMARY KEY,
    tenant_id TEXT NOT NULL,
    term_name TEXT NOT NULL,
    academic_year TEXT NOT NULL,
    start_date TEXT NOT NULL,
    end_date TEXT NOT NULL,
    grace_period_days INTEGER DEFAULT 7,
    max_students INTEGER DEFAULT 1000,
    max_staff INTEGER DEFAULT 200,
    license_key TEXT NOT NULL,
    signed_license_token TEXT NOT NULL,
    status TEXT DEFAULT 'ACTIVE',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

2. CRYPTOGRAPHIC LICENSING ENGINE (SUPERADMIN CA)

2.1 Ed25519 Asymmetric Key Generation & Signing Service

File to Update: E:\nextgen\next-gen\superadmin\server\src\services\LicenseTokenGenerator.js

/**
 * LicenseTokenGenerator.js
 * Acts as the Certificate Authority (CA) using Ed25519 (EdDSA) asymmetric signing.
 */
const crypto = require('crypto');
const jwt = require('jsonwebtoken');

class LicenseTokenGenerator {
  /**
   * Generates or retrieves the Ed25519 Key Pair
   */
  static getKeyPair() {
    const privateKeyPem = process.env.SUPERADMIN_PRIVATE_KEY;
    const publicKeyPem = process.env.SUPERADMIN_PUBLIC_KEY;

    if (privateKeyPem && publicKeyPem) {
      return { privateKey: privateKeyPem, publicKey: publicKeyPem };
    }

    // Fallback keypair generation for development if env variables are not supplied
    const { privateKey, publicKey } = crypto.generateKeyPairSync('ed25519', {
      privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
      publicKeyEncoding: { type: 'spki', format: 'pem' }
    });

    return { privateKey, publicKey };
  }

  /**
   * Generate an Ed25519 cryptographically signed license token
   */
  static generateToken(payload) {
    const { privateKey } = this.getKeyPair();

    const nowSeconds = Math.floor(Date.now() / 1000);
    const nbf = payload.nbf || Math.floor(new Date(payload.start_date).getTime() / 1000);
    const exp = payload.exp || Math.floor(new Date(payload.end_date).getTime() / 1000);

    const licensePayload = {
      iss: 'super_admin_licensing_engine',
      sub: payload.tenant_id,
      tenant_code: payload.tenant_code,
      term: payload.term_name,
      academic_year: payload.academic_year,
      max_students: payload.max_students || 1000,
      max_staff: payload.max_staff || 200,
      nbf: nbf,
      exp: exp,
      status: 'active',
      iat: nowSeconds
    };

    // Sign payload using Ed25519 (EdDSA algorithm in JWT standard)
    const token = jwt.sign(licensePayload, privateKey, {
      algorithm: 'EdDSA'
    });

    const keyFormatted = `LIC-${(payload.term_name || 'TERM').toUpperCase().replace(/\s+/g, '')}-${payload.academic_year}-${token.slice(-16).toUpperCase()}`;

    return {
      token,
      license_key: keyFormatted,
      payload: licensePayload
    };
  }

  /**
   * Verify an Ed25519 token using the Public Key
   */
  static verifyToken(token) {
    const { publicKey } = this.getKeyPair();
    try {
      const decoded = jwt.verify(token, publicKey, { algorithms: ['EdDSA'] });
      return { isValid: true, payload: decoded };
    } catch (err) {
      return { isValid: false, error: err.message };
    }
  }
}

module.exports = LicenseTokenGenerator;

2.2 SuperAdmin JWKS & Public Key Distribution Endpoint

File to Update: E:\nextgen\next-gen\superadmin\server\src\routes\superadmin.routes.js

Add route /api/v1/auth/jwks to expose the Ed25519 Public Key for online tenant bootstrapping:

router.get('/v1/auth/jwks', (req, res) => {
  const { publicKey } = LicenseTokenGenerator.getKeyPair();
  res.json({
    keys: [
      {
        kty: 'OKP',
        crv: 'Ed25519',
        use: 'sig',
        alg: 'EdDSA',
        pubKeyPem: publicKey
      }
    ]
  });
});

3. HYBRID DUAL-DRIVER CACHING LAYER (TENANT BACKEND)

File to Create: E:\nextgen\next-gen\server\src\services\LicenseCacheService.js

/**
 * LicenseCacheService.js
 * Hybrid Dual-Driver Cache (In-Memory LRU default + Redis Adapter)
 */
const { LRUCache } = require('lru-cache');
const redis = require('redis');

// In-Memory LRU Cache Instance (default zero-dependency fallback)
const localMemoryCache = new LRUCache({
  max: 500, // Maximum 500 cached tenant tokens
  ttl: 1000 * 60 * 60 * 2 // 2 hours default local memory check interval
});

let redisClient = null;

if (process.env.REDIS_URL) {
  redisClient = redis.createClient({ url: process.env.REDIS_URL });
  redisClient.on('error', (err) => console.error('Redis License Cache Error:', err));
  redisClient.connect().catch(() => {
    console.warn('⚠️ Could not connect to Redis; defaulting to in-memory LRU cache.');
  });
}

const LicenseCache = {
  async get(tenantId) {
    try {
      if (redisClient && redisClient.isOpen) {
        const data = await redisClient.get(`license:tenant:${tenantId}`);
        return data ? JSON.parse(data) : null;
      }
    } catch (e) {
      console.warn('Redis GET failed, falling back to LRU cache:', e.message);
    }
    return localMemoryCache.get(tenantId) || null;
  },

  async set(tenantId, payload, ttlInSeconds) {
    try {
      if (redisClient && redisClient.isOpen) {
        await redisClient.set(`license:tenant:${tenantId}`, JSON.stringify(payload), {
          EX: Math.max(ttlInSeconds, 60)
        });
        return;
      }
    } catch (e) {
      console.warn('Redis SET failed, falling back to LRU cache:', e.message);
    }
    localMemoryCache.set(tenantId, payload, { ttl: Math.max(ttlInSeconds, 60) * 1000 });
  },

  async del(tenantId) {
    if (redisClient && redisClient.isOpen) {
      await redisClient.del(`license:tenant:${tenantId}`);
    }
    localMemoryCache.delete(tenantId);
  }
};

module.exports = LicenseCache;

4. GLOBAL ACCESSMIDDLEWARE & CLOCK DRIFT PROTECTION

File to Update: E:\nextgen\next-gen\server\src\middleware\licensingMiddleware.js

/**
 * licensingMiddleware.js
 * Intercepts incoming requests, verifies Ed25519 tokens via Hybrid Cache,
 * checks system clock drift, and enforces Read-Only restriction (HTTP 402) on expired licenses.
 */
const jwt = require('jsonwebtoken');
const Database = require('better-sqlite3');
const path = require('path');
const LicenseCache = require('../services/LicenseCacheService');

// Public Key injected via env or fetched from JWKS
const SUPERADMIN_PUBLIC_KEY = process.env.SUPERADMIN_PUBLIC_KEY || `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA...
-----END PUBLIC KEY-----`;

let lastSuperAdminTimeSync = Date.now();
let serverTimeDriftOffsetMs = 0;

// Track trusted time sync with SuperAdmin
function updateSystemClockSync(superAdminTimestamp) {
  if (superAdminTimestamp) {
    const remoteTime = new Date(superAdminTimestamp).getTime();
    serverTimeDriftOffsetMs = Math.abs(Date.now() - remoteTime);
    lastSuperAdminTimeSync = Date.now();
  }
}

function verifyEd25519Token(token) {
  try {
    const decoded = jwt.verify(token, SUPERADMIN_PUBLIC_KEY, { algorithms: ['EdDSA'] });
    return { isValid: true, payload: decoded };
  } catch (err) {
    return { isValid: false, error: err.message };
  }
}

async function verifyLicenseMiddleware(req, res, next) {
  const tenantId =
    req.headers['x-tenant-id'] ||
    req.headers['x-tenant-code'] ||
    process.env.TENANT_ID ||
    '5b39bcb5-506a-456d-b7cb-91730b397cc1';

  req.tenantId = tenantId;

  // Bypass routes (Auth, SysAdmin, Health, Billing)
  const PUBLIC_PATHS = ['/api/auth/login', '/api/auth/refresh', '/api/sysadmin', '/api/health', '/api/billing'];
  if (PUBLIC_PATHS.some(p => req.path.startsWith(p)) || req.user?.role === 'systems_admin') {
    return next();
  }

  // Anti-Clock-Tampering Guard: Lock mutations if system clock has drifted > 5 minutes (300,000 ms)
  if (serverTimeDriftOffsetMs > 300000) {
    console.error(`🚨 System Clock Drift Detected: ${serverTimeDriftOffsetMs}ms variance.`);
    return blockMutationRequests(req, res, 'CLOCK_DRIFT_DETECTED', 'System clock tampering or significant time drift detected.');
  }

  try {
    // 1. Query Hybrid Cache (Memory LRU / Redis)
    let cachedPayload = await LicenseCache.get(tenantId);

    // 2. Cache Miss Fallback: Read DB raw signed token & verify Ed25519 signature
    if (!cachedPayload) {
      const dbPath = process.env.DB_PATH || path.join(__dirname, '../../data/school.db');
      const db = new Database(dbPath);
      const row = db.prepare('SELECT * FROM tenant_licenses WHERE tenant_id = ? AND status = "ACTIVE" ORDER FROM rowid DESC LIMIT 1').get(tenantId);
      db.close();

      if (!row || !row.signed_license_token) {
        return blockMutationRequests(req, res, 'NO_LICENSE', 'No active license token found for tenant.');
      }

      const verification = verifyEd25519Token(row.signed_license_token);
      if (!verification.isValid) {
        return blockMutationRequests(req, res, 'INVALID_SIGNATURE', 'License signature verification failed.');
      }

      cachedPayload = verification.payload;
      const nowSec = Math.floor(Date.now() / 1000);
      const remainingTTL = (cachedPayload.exp || 0) - nowSec;

      if (remainingTTL > 0) {
        await LicenseCache.set(tenantId, cachedPayload, remainingTTL);
      }
    }

    // 3. Temporal Bound Check
    const currentSeconds = Math.floor(Date.now() / 1000);
    if (currentSeconds < cachedPayload.nbf || currentSeconds > cachedPayload.exp) {
      return blockMutationRequests(req, res, 'LICENSE_EXPIRED', 'Your termly license has expired.');
    }

    // License is valid
    res.setHeader('X-License-Status', 'ACTIVE');
    return next();
  } catch (err) {
    console.error('License Middleware Error:', err);
    return blockMutationRequests(req, res, 'EVALUATION_ERROR', err.message);
  }
}

function blockMutationRequests(req, res, code, message) {
  const isReadOnly = ['GET', 'HEAD', 'OPTIONS'].includes(req.method.toUpperCase());

  if (isReadOnly) {
    res.setHeader('X-License-Status', 'Expired-Read-Only');
    return req.next ? req.next() : res.status(200);
  }

  return res.status(402).json({
    error: 'Payment Required / Term License Expired',
    code: code || 'LICENSE_EXPIRED',
    message: message || 'Your termly license has expired. Please contact billing.',
    billing_url: '/billing'
  });
}

module.exports = {
  verifyLicenseMiddleware,
  updateSystemClockSync
};

5. CLIENT-SIDE PWA OFFLINE VERIFICATION (REACT VITE)

Location: E:\nextgen\next-gen\client\src\services\offlineLicenseVerifier.js

/**
 * offlineLicenseVerifier.js
 * Uses Browser Native Web Crypto API to verify Ed25519 license tokens offline in React PWA.
 */
export async function verifyLicenseOffline(signedToken, publicKeyPem) {
  try {
    const [headerB64, payloadB64, signatureB64] = signedToken.split('.');
    const dataToVerify = new TextEncoder().encode(`${headerB64}.${payloadB64}`);
    
    // Decode base64url signature
    const signatureBin = Uint8Array.from(atob(signatureB64.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
    const payloadJson = JSON.parse(atob(payloadB64.replace(/-/g, '+').replace(/_/g, '/')));

    // Simple temporal validation
    const nowSeconds = Math.floor(Date.now() / 1000);
    if (nowSeconds < payloadJson.nbf || nowSeconds > payloadJson.exp) {
      return { isValid: false, reason: 'EXPIRED', payload: payloadJson };
    }

    return { isValid: true, payload: payloadJson };
  } catch (e) {
    return { isValid: false, reason: 'INVALID_FORMAT', error: e.message };
  }
}

6. STEP-BY-STEP EXECUTION AGENT CHECKLIST

  1. Update Supabase Database Schema (E:\nextgen\supabase\supabase_full_schema.sql):
    • Ensure tenant_licenses table includes signed_license_token TEXT and algorithm TEXT DEFAULT 'EdDSA'.
    • Apply schema updates to Supabase PostgreSQL cloud instance via Supabase CLI or SQL Editor:
      supabase db push --schema-file E:\nextgen\supabase\supabase_full_schema.sql
      
  2. Update SuperAdmin CA Generator (E:\nextgen\next-gen\superadmin\server\src\services\LicenseTokenGenerator.js):
    • Replace symmetric HS256 with Ed25519 (EdDSA) signing using crypto.generateKeyPairSync('ed25519') or PEM environment keys.
  3. Expose JWKS Endpoint (E:\nextgen\next-gen\superadmin\server\src\routes\superadmin.routes.js):
    • Add /api/v1/auth/jwks route returning public keys.
  4. Implement Hybrid Dual-Driver Cache (E:\nextgen\next-gen\server\src\services\LicenseCacheService.js):
    • Implement in-memory LRU (lru-cache) with optional Redis fallback.
  5. Update Tenant AccessMiddleware (E:\nextgen\next-gen\server\src\middleware\licensingMiddleware.js):
    • Integrate LicenseCacheService.
    • Add Ed25519 asymmetric verification.
    • Enforce HTTP 402 Payment Required for non-GET/HEAD/OPTIONS requests on expired licenses.
    • Add 5-minute system clock drift threshold check.
  6. Configure First Tenant Environment (E:\nextgen\next-gen\.env & E:\nextgen\next-gen\server\.env):
    • Populate SUPERADMIN_PUBLIC_KEY and VITE_SUPERADMIN_PUBLIC_KEY.
  7. Run Verification Commands:
    • Test license creation in SuperAdmin UI and confirm sync to Supabase tenant_licenses table.
    • Test GET vs POST endpoints on E:\nextgen\next-gen\server under active vs expired license tokens.