# Edge-Net Relay Security Architecture ## System Overview ``` ┌─────────────────────────────────────────────────────────────────┐ │ Edge-Net Security Layers │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Layer 1: Connection Security │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ • Origin Validation (CORS) │ │ │ │ • Connection Limits (5 per IP) │ │ │ │ • Heartbeat Timeout (30s) │ │ │ └────────────────────────────────────────────────────────┘ │ │ ↓ │ │ Layer 2: Message Security │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ • Rate Limiting (100 msg/min per node) │ │ │ │ • Message Size Limits (64KB max) │ │ │ │ • Message Type Validation │ │ │ └────────────────────────────────────────────────────────┘ │ │ ↓ │ │ Layer 3: Identity Security (⚠️ NEEDS IMPROVEMENT) │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ • Public Key Registration │ │ │ │ • ❌ Signature Verification (NOT IMPLEMENTED) │ │ │ │ • ⚠️ No Proof of Key Ownership │ │ │ └────────────────────────────────────────────────────────┘ │ │ ↓ │ │ Layer 4: Task Security (✅ SECURE) │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ • Assignment Tracking (Map-based) │ │ │ │ • Node ID Verification │ │ │ │ • Replay Prevention (Set-based) │ │ │ │ • Task Expiration (5 min) │ │ │ └────────────────────────────────────────────────────────┘ │ │ ↓ │ │ Layer 5: Credit Security (✅ SECURE) │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ • QDAG Firestore Ledger (Source of Truth) │ │ │ │ • Server-Only Crediting │ │ │ │ • Credit Self-Reporting BLOCKED │ │ │ │ • Public Key-Based Ledger │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## Task Lifecycle Security ``` ┌─────────────┐ │ Submitter │ │ (Node A) │ └──────┬──────┘ │ │ 1. task_submit │ (task details + maxCredits) ↓ ┌─────────────────────────────────────┐ │ Relay Server │ │ ┌──────────────────────────────┐ │ │ │ 2. Generate Task ID │ │ │ │ task-{timestamp}-{random} │ │ │ └──────────────┬───────────────┘ │ │ │ │ │ │ 3. Add to taskQueue│ │ ↓ │ │ ┌──────────────────────────────┐ │ │ │ 4. Select Random Worker │ │ │ │ (from connected nodes) │ │ │ └──────────────┬───────────────┘ │ │ │ │ │ │ 5. Store Assignment│ │ │ assignedTasks.set(taskId, { │ │ assignedTo: workerNodeId, │ │ assignedToPublicKey: workerPubKey, │ │ submitter: submitterNodeId, │ │ maxCredits: credits, │ │ assignedAt: timestamp │ │ }) │ ↓ │ └─────────────────┼────────────────────┘ │ │ 6. task_assignment ↓ ┌──────────────┐ │ Worker │ │ (Node B) │ └──────┬───────┘ │ │ 7. Processes task │ │ 8. task_complete │ (taskId + result + reward) ↓ ┌─────────────────────────────────────┐ │ Relay Server │ │ ┌──────────────────────────────┐ │ │ │ 9. SECURITY CHECKS: │ │ │ │ ✓ Task in assignedTasks? │ │ │ │ ✓ Node ID matches? │ │ │ │ ✓ Not already completed? │ │ │ │ ✓ Public key exists? │ │ │ └──────────────┬───────────────┘ │ │ │ │ │ │ 10. Mark Complete │ │ │ completedTasks.add(taskId) │ │ assignedTasks.delete(taskId) │ ↓ │ │ ┌──────────────────────────────┐ │ │ │ 11. Credit Worker (Firestore)│ │ │ │ creditAccount( │ │ │ │ publicKey, │ │ │ │ rewardAmount, │ │ │ │ taskId │ │ │ │ ) │ │ │ └──────────────┬───────────────┘ │ │ │ │ │ │ 12. Notify Worker │ │ │ credit_earned │ │ │ │ │ │ 13. Notify Submitter│ │ │ task_result │ └─────────────────┼────────────────────┘ │ ↓ ┌────────────────┐ │ QDAG Ledger │ │ (Firestore) │ │ │ │ publicKey: │ │ earned: +X │ │ tasks: +1 │ └────────────────┘ ``` --- ## Attack Vector Protections ### 1. Task Completion Spoofing Attack ``` ATTACK SCENARIO: ┌─────────────┐ ┌─────────────┐ │ Attacker │ │ Victim │ │ (Node C) │ │ (Node B) │ └──────┬──────┘ └──────┬──────┘ │ │ │ │ Assigned task-123 │ │ │ ❌ task_complete (task-123) │ │ "I completed it!" │ ↓ ↓ ┌─────────────────────────────────────────────────┐ │ Relay Server │ │ ┌──────────────────────────────────────────┐ │ │ │ SECURITY CHECK: │ │ │ │ assignedTasks.get('task-123') │ │ │ │ → { assignedTo: 'node-b' } │ │ │ │ │ │ │ │ if (assignedTo !== attackerNodeId) { │ │ │ │ ❌ REJECT: "Not assigned to you" │ │ │ │ } │ │ │ └──────────────────────────────────────────┘ │ └─────────────────────────────────────────────────┘ RESULT: ✅ Attack BLOCKED ``` ### 2. Replay Attack ``` ATTACK SCENARIO: ┌─────────────┐ │ Worker │ │ (Node B) │ └──────┬──────┘ │ │ ✅ task_complete (task-123) → CREDITED 1000 rUv │ │ Wait 1 second... │ │ ❌ task_complete (task-123) → TRY AGAIN for another 1000 rUv ↓ ┌─────────────────────────────────────────────────┐ │ Relay Server │ │ ┌──────────────────────────────────────────┐ │ │ │ FIRST COMPLETION: │ │ │ │ 1. Verify assignment ✅ │ │ │ │ 2. completedTasks.add('task-123') │ │ │ │ 3. assignedTasks.delete('task-123') │ │ │ │ 4. creditAccount() ✅ │ │ │ └──────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────┐ │ │ │ SECOND COMPLETION (REPLAY): │ │ │ │ 1. Check: completedTasks.has(task-123) │ │ │ │ → TRUE │ │ │ │ 2. ❌ REJECT: "Already completed" │ │ │ └──────────────────────────────────────────┘ │ └─────────────────────────────────────────────────┘ RESULT: ✅ Replay Attack BLOCKED ``` ### 3. Credit Self-Reporting Attack ``` ATTACK SCENARIO: ┌─────────────┐ │ Attacker │ │ (Node C) │ └──────┬──────┘ │ │ ❌ ledger_update │ { │ publicKey: "my-key", │ ledger: { │ earned: 999999999, ← FAKE! │ spent: 0 │ } │ } ↓ ┌─────────────────────────────────────────────────┐ │ Relay Server │ │ ┌──────────────────────────────────────────┐ │ │ │ case 'ledger_update': │ │ │ │ console.warn("REJECTED") │ │ │ │ ws.send({ │ │ │ │ type: 'error', │ │ │ │ message: 'Credit self-reporting │ │ │ │ disabled' │ │ │ │ }) │ │ │ │ ❌ RETURN (no action taken) │ │ │ └──────────────────────────────────────────┘ │ └─────────────────────────────────────────────────┘ │ │ Only way to earn credits: ↓ ┌─────────────────────────────────────────────────┐ │ Complete assigned task → relay calls │ │ creditAccount() → Firestore updated │ └─────────────────────────────────────────────────┘ RESULT: ✅ Self-Reporting BLOCKED ``` ### 4. Public Key Spoofing Attack ``` ATTACK SCENARIO: ┌─────────────┐ ┌─────────────┐ │ Attacker │ │ Victim │ │ (Node C) │ │ (Node V) │ │ │ │ │ │ pubKey: │ │ pubKey: │ │ "victim-pk" │ ← SPOOFED │ "victim-pk" │ └──────┬──────┘ └─────────────┘ │ │ Register with victim's public key ↓ ┌─────────────────────────────────────────────────┐ │ Relay Server │ │ ┌──────────────────────────────────────────┐ │ │ │ CURRENT PROTECTION (Limited): │ │ │ │ ⚠️ Registration allowed │ │ │ │ (no signature verification) │ │ │ │ ✅ Credits assigned at task time │ │ │ │ (uses publicKey from assignment) │ │ │ └──────────────────────────────────────────┘ │ └─────────────────────────────────────────────────┘ CURRENT SECURITY: ✅ Attacker CANNOT steal victim's existing credits (ledger keyed by public key - victim still has their balance) ✅ Attacker CANNOT complete victim's assigned tasks (checked by NODE ID, not public key) ⚠️ Attacker CAN check victim's balance (ledger_sync is read-only, returns balance for any public key) ⚠️ Attacker working benefits victim (credits earned go to victim's public key) NEEDED IMPROVEMENT: ❌ Add Ed25519 signature verification ❌ Challenge-response on registration ❌ Signature required on sensitive operations RESULT: ⚠️ Partially Protected (needs signatures) ``` --- ## QDAG Ledger Architecture ``` ┌───────────────────────────────────────────────────────────┐ │ QDAG Credit System │ │ (Quantum Directed Acyclic Graph Ledger) │ └───────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────┐ │ Firestore Database │ │ (SOURCE OF TRUTH) │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Collection: edge-net-qdag │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ Document: {publicKey} │ │ │ │ │ │ { │ │ │ │ │ │ earned: 5000000, ← Can only increase │ │ │ │ │ │ spent: 1000000, │ │ │ │ │ │ tasksCompleted: 5, │ │ │ │ │ │ createdAt: 1234567890, │ │ │ │ │ │ updatedAt: 1234567899, │ │ │ │ │ │ lastTaskId: "task-123-xyz" │ │ │ │ │ │ } │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ └───────────────────────────────────────────────────────┘ │ └─────────────────┬───────────────────────────────────────────┘ │ │ Synced via ↓ ┌─────────────────────────────────────────────────────────────┐ │ Relay Server (In-Memory Cache) │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ ledgerCache = new Map() │ │ │ │ "pubkey1" → { earned: 5000000, spent: 1000000 } │ │ │ │ "pubkey2" → { earned: 2000000, spent: 500000 } │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Credit Functions (SERVER ONLY): │ │ │ │ │ │ │ │ async function creditAccount(pubKey, amount, taskId) │ │ │ │ ✅ ONLY way to increase credits │ │ │ │ ✅ ONLY called after verified task completion │ │ │ │ ✅ Writes to Firestore (source of truth) │ │ │ │ │ │ │ │ async function debitAccount(pubKey, amount, reason) │ │ │ │ ✅ For spending credits │ │ │ │ ✅ Checks balance before debiting │ │ │ └───────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ │ Clients can only: ↓ ┌─────────────────────────────────────────────────────────────┐ │ Client Nodes │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ ledger_sync (read-only) │ │ │ │ → Returns balance from Firestore │ │ │ │ │ │ │ │ ledger_update (BLOCKED) │ │ │ │ → Error: "Credit self-reporting disabled" │ │ │ │ │ │ │ │ task_complete (after assignment) │ │ │ │ → Relay verifies → calls creditAccount() │ │ │ └───────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ KEY SECURITY PROPERTIES: ✅ Credits keyed by PUBLIC KEY (identity, not node) ✅ Same public key = same balance across devices ✅ Server-side ONLY credit increases ✅ Firestore is single source of truth ✅ Clients cannot self-report credits ✅ Persistent across sessions ✅ Atomic operations with cache ``` --- ## Security Control Matrix | Control | Type | Status | Risk Mitigated | Code Location | |---------|------|--------|----------------|---------------| | Origin Validation | Preventive | ✅ Implemented | CSRF, unauthorized access | Lines 255-263 | | Connection Limits | Preventive | ✅ Implemented | DoS (single IP) | Lines 308-315 | | Rate Limiting | Preventive | ✅ Implemented | Message flooding | Lines 265-279 | | Message Size Limits | Preventive | ✅ Implemented | DoS (large payloads) | Lines 338-342 | | Heartbeat Timeout | Preventive | ✅ Implemented | Zombie connections | Lines 320-329 | | Task Assignment Tracking | Detective | ✅ Implemented | Completion spoofing | Lines 222-229 | | Assignment Verification | Preventive | ✅ Implemented | Completion spoofing | Lines 411-423 | | Replay Prevention | Preventive | ✅ Implemented | Replay attacks | Lines 425-430 | | Self-Report Blocking | Preventive | ✅ Implemented | Credit fraud | Lines 612-622 | | Server-Only Crediting | Preventive | ✅ Implemented | Credit fraud | Lines 119-129 | | QDAG Ledger | Preventive | ✅ Implemented | Credit tampering | Lines 66-117 | | Task Expiration | Preventive | ✅ Implemented | Stale assignments | Lines 243-253 | | Signature Verification | Preventive | ❌ NOT Implemented | Identity spoofing | Lines 281-286 | | Challenge-Response | Preventive | ❌ NOT Implemented | Registration fraud | N/A | | Global Conn Limit | Preventive | ❌ NOT Implemented | Distributed DoS | N/A | --- ## Security Metrics Dashboard ``` ┌─────────────────────────────────────────────────────────────┐ │ Edge-Net Security Metrics │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Overall Security Score: 85/100 │ │ ████████████████████████████████░░░░░░░░ │ │ │ │ Authentication: 50/100 ⚠️ │ │ ██████████████████████░░░░░░░░░░░░░░░░░░ │ │ (Missing signature verification) │ │ │ │ Authorization: 100/100 ✅ │ │ ████████████████████████████████████████ │ │ (Task assignment verification excellent) │ │ │ │ Credit System: 100/100 ✅ │ │ ████████████████████████████████████████ │ │ (QDAG ledger architecture excellent) │ │ │ │ DoS Protection: 80/100 ✅ │ │ ████████████████████████████████░░░░░░░░ │ │ (Missing global connection limit) │ │ │ │ Data Integrity: 100/100 ✅ │ │ ████████████████████████████████████████ │ │ (Firestore source of truth) │ │ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## Recommendations Priority Matrix ``` ┌───────────────────────────────────────────────────────────────┐ │ Impact vs Effort Matrix │ │ │ │ High Impact │ │ │ ↑ │ 🔴 Signature │ │ │ │ │ Verification │ │ │ │ │ (CRITICAL) │ 🟡 Global Conn │ │ │ │ │ Limit │ │ │ ├──────────────────────┼──────────────────────────┤ │ │ │ │ │ │ │ │ 🟡 Completed Tasks │ 🟢 Error Messages │ │ │ │ Cleanup │ Generic │ │ │ │ │ │ │ Low Impact │ │ │ │ └──────────────────────┴──────────────────────────┘ │ Low Effort → → → High Effort │ └───────────────────────────────────────────────────────────────┘ Legend: 🔴 Critical Priority (Do before production) 🟡 Medium Priority (Do within 1-2 weeks) 🟢 Low Priority (Nice to have) ``` --- ## Audit Trail Example ``` Typical Secure Task Flow (with all security checks): 2026-01-03 10:15:23 [Relay] Node registered: node-abc-123 with identity 8a7f3d2e... 2026-01-03 10:15:24 [Relay] Task submitted: task-456-xyz from node-abc-123 2026-01-03 10:15:25 [Relay] Assigned task task-456-xyz to node-def-789 ↳ Assignment: { assignedTo: 'node-def-789', assignedToPublicKey: '8a7f3d2e...', submitter: 'node-abc-123', maxCredits: 2000000, assignedAt: 1735900525000 } 2026-01-03 10:17:30 [Relay] Task task-456-xyz VERIFIED completed by node-def-789 ↳ Security checks passed: ✅ Task in assignedTasks ✅ Node ID matches assignment ✅ Not already completed ✅ Public key available 2026-01-03 10:17:31 [QDAG] Credited 0.002 rUv to 8a7f3d2e... for task task-456-xyz 2026-01-03 10:17:32 [QDAG] Saved ledger for 8a7f3d2e...: earned=0.005 Rejected Attack Example: 2026-01-03 10:20:15 [SECURITY] Task task-456-xyz was assigned to node-def-789, not node-evil-999 - SPOOFING ATTEMPT 2026-01-03 10:20:15 [Relay] Rejected task_complete from node-evil-999 (not assigned) 2026-01-03 10:20:20 [SECURITY] Task task-456-xyz already completed - REPLAY ATTEMPT from node-def-789 2026-01-03 10:20:20 [Relay] Rejected duplicate task_complete 2026-01-03 10:20:25 [QDAG] REJECTED ledger_update from node-evil-999 - clients cannot self-report credits ``` --- **Document Version**: 1.0 **Last Updated**: 2026-01-03 **Related Documents**: - Full Security Audit Report: `SECURITY_AUDIT_REPORT.md` - Quick Reference: `SECURITY_QUICK_REFERENCE.md` - Test Suite: `/tests/relay-security.test.ts`