13 KiB
Plaid Local Learning System
Privacy-preserving financial intelligence that runs 100% in the browser
Overview
The Plaid Local Learning System enables sophisticated financial analysis and machine learning while keeping all data on the user's device. No financial information, learned patterns, or AI models ever leave the browser.
Architecture
┌─────────────────────────────────────────────────────────────────────────────┐
│ USER'S BROWSER (All Data Stays Here) │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌───────────────────┐ │
│ │ Plaid Link │────▶│ Transaction │────▶│ Local Learning │ │
│ │ (OAuth) │ │ Processor │ │ Engine (WASM) │ │
│ └─────────────────┘ └──────────────────┘ └───────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌───────────────────┐ │
│ │ IndexedDB │ │ IndexedDB │ │ IndexedDB │ │
│ │ (Tokens) │ │ (Embeddings) │ │ (Q-Values) │ │
│ └─────────────────┘ └──────────────────┘ └───────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ RuVector WASM Engine │ │
│ │ │ │
│ │ • HNSW Vector Index ─────── 150x faster similarity search │ │
│ │ • Spiking Neural Network ── Temporal pattern learning (STDP) │ │
│ │ • Q-Learning ────────────── Spending optimization │ │
│ │ • LSH (Locality-Sensitive)─ Semantic categorization │ │
│ │ • Anomaly Detection ─────── Statistical outlier detection │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
│
│ HTTPS (only OAuth + API calls)
▼
┌─────────────────────┐
│ Plaid Servers │
│ (Auth & Raw Data) │
└─────────────────────┘
Privacy Guarantees
| Guarantee | Description |
|---|---|
| 🔒 No Data Exfiltration | Financial transactions never leave the browser |
| 🧠 Local-Only Learning | All ML models train and run in WebAssembly |
| 🔐 Encrypted Storage | Optional AES-256-GCM encryption for IndexedDB |
| 📊 No Analytics | Zero tracking, telemetry, or data collection |
| 🌐 Offline-Capable | Works without network after initial Plaid sync |
| 🗑️ User Control | Instant, complete data deletion on request |
Features
1. Smart Transaction Categorization
ML-based categorization using semantic embeddings and HNSW similarity search.
const prediction = learner.predictCategory(transaction);
// { category: "Food and Drink", confidence: 0.92, similar_transactions: [...] }
2. Anomaly Detection
Identify unusual transactions compared to learned spending patterns.
const anomaly = learner.detectAnomaly(transaction);
// { is_anomaly: true, anomaly_score: 2.3, reason: "Amount $500 is 5x typical", expected_amount: 100 }
3. Budget Recommendations
Q-learning based budget optimization that improves over time.
const recommendation = learner.getBudgetRecommendation("Food", currentSpending, budget);
// { category: "Food", recommended_limit: 450, current_avg: 380, trend: "stable", confidence: 0.85 }
4. Temporal Pattern Analysis
Understand weekly and monthly spending habits.
const heatmap = learner.getTemporalHeatmap();
// { day_of_week: [100, 50, 60, 80, 120, 200, 180], day_of_month: [...] }
5. Similar Transaction Search
Find transactions similar to a given one using vector similarity.
const similar = learner.findSimilar(transaction, 5);
// [{ id: "tx_123", distance: 0.05 }, { id: "tx_456", distance: 0.12 }, ...]
Quick Start
Installation
npm install @ruvector/edge
Basic Usage
import { PlaidLocalLearner } from '@ruvector/edge';
// Initialize (loads WASM, opens IndexedDB)
const learner = new PlaidLocalLearner();
await learner.init();
// Optional: Use encryption password
await learner.init('your-secure-password');
// Process transactions from Plaid
const insights = await learner.processTransactions(transactions);
console.log(`Processed ${insights.transactions_processed} transactions`);
console.log(`Learned ${insights.patterns_learned} patterns`);
// Get analysis
const category = learner.predictCategory(newTransaction);
const anomaly = learner.detectAnomaly(newTransaction);
const budget = learner.getBudgetRecommendation("Groceries", 320, 400);
// Record user feedback for Q-learning
learner.recordOutcome("Groceries", "under_budget", 1.0);
// Save state (persists to IndexedDB)
await learner.save();
// Export for backup
const backup = await learner.exportData();
// Clear all data (privacy feature)
await learner.clearAllData();
With Plaid Link
import { PlaidLocalLearner, PlaidLinkHandler } from '@ruvector/edge';
// Initialize Plaid Link handler
const plaidHandler = new PlaidLinkHandler({
environment: 'sandbox',
products: ['transactions'],
countryCodes: ['US'],
language: 'en',
});
await plaidHandler.init();
// After successful Plaid Link flow, store token locally
await plaidHandler.storeToken(itemId, accessToken);
// Later: retrieve token for API calls
const token = await plaidHandler.getToken(itemId);
Machine Learning Components
HNSW Vector Index
- Purpose: Fast similarity search for transaction categorization
- Performance: 150x faster than brute-force search
- Memory: Sub-linear space complexity
Q-Learning
- Purpose: Optimize budget recommendations over time
- Algorithm: Temporal difference learning with ε-greedy exploration
- Learning Rate: 0.1 (configurable)
- States: Category + spending ratio
- Actions: under_budget, at_budget, over_budget
Spiking Neural Network
- Purpose: Temporal pattern recognition (weekday vs weekend spending)
- Architecture: 21 input → 32 hidden → 8 output neurons
- Learning: Spike-Timing Dependent Plasticity (STDP)
Feature Extraction
Each transaction is converted to a 21-dimensional feature vector:
- Amount (log-normalized)
- Day of week (0-6)
- Day of month (1-31)
- Hour of day (0-23)
- Weekend indicator
- Category LSH hash (8 dims)
- Merchant LSH hash (8 dims)
Data Storage
IndexedDB Schema
| Store | Key | Value | Purpose |
|---|---|---|---|
learning_state |
main |
Encrypted JSON | Q-values, patterns, embeddings |
plaid_tokens |
Item ID | Access token | Plaid API authentication |
transactions |
Transaction ID | Transaction | Raw transaction storage |
insights |
Date | Insights | Daily aggregated insights |
Storage Limits
- IndexedDB quota: ~50MB - 1GB (browser dependent)
- Typical usage: ~1KB per 100 transactions
- Learning state: ~10KB for 1000 patterns
Security Considerations
Encryption
// Initialize with encryption
await learner.init('user-password');
// Password is never stored
// PBKDF2 key derivation (100,000 iterations)
// AES-256-GCM encryption for all stored data
Token Storage
// Plaid tokens are stored in IndexedDB
// Never sent to any third party
// Automatically cleared with clearAllData()
Cross-Origin Isolation
The WASM module runs in the browser's sandbox with no network access. Only the JavaScript wrapper can make network requests (to Plaid).
API Reference
PlaidLocalLearner
| Method | Description |
|---|---|
init(password?) |
Initialize WASM and IndexedDB |
processTransactions(tx[]) |
Process and learn from transactions |
predictCategory(tx) |
Predict category for transaction |
detectAnomaly(tx) |
Check if transaction is anomalous |
getBudgetRecommendation(cat, spent, budget) |
Get budget advice |
recordOutcome(cat, action, reward) |
Record for Q-learning |
getPatterns() |
Get all learned patterns |
getTemporalHeatmap() |
Get spending heatmap |
findSimilar(tx, k) |
Find similar transactions |
getStats() |
Get learning statistics |
save() |
Persist state to IndexedDB |
load() |
Load state from IndexedDB |
exportData() |
Export encrypted backup |
importData(data) |
Import from backup |
clearAllData() |
Delete all local data |
Types
interface Transaction {
transaction_id: string;
account_id: string;
amount: number;
date: string; // YYYY-MM-DD
name: string;
merchant_name?: string;
category: string[];
pending: boolean;
payment_channel: string;
}
interface SpendingPattern {
pattern_id: string;
category: string;
avg_amount: number;
frequency_days: number;
confidence: number; // 0-1
last_seen: number; // timestamp
}
interface CategoryPrediction {
category: string;
confidence: number;
similar_transactions: string[];
}
interface AnomalyResult {
is_anomaly: boolean;
anomaly_score: number; // 0 = normal, >1 = anomalous
reason: string;
expected_amount: number;
}
interface BudgetRecommendation {
category: string;
recommended_limit: number;
current_avg: number;
trend: 'increasing' | 'stable' | 'decreasing';
confidence: number;
}
interface LearningStats {
version: number;
patterns_count: number;
q_values_count: number;
embeddings_count: number;
index_size: number;
}
Performance
| Metric | Value | Notes |
|---|---|---|
| WASM Load | ~50ms | First load, cached after |
| Process 100 tx | ~10ms | Vector indexing + learning |
| Category Prediction | <1ms | HNSW search |
| Anomaly Detection | <1ms | Pattern lookup |
| IndexedDB Save | ~5ms | Async, non-blocking |
| Memory Usage | ~2-5MB | Depends on index size |
Browser Compatibility
| Browser | Status | Notes |
|---|---|---|
| Chrome 80+ | ✅ Full Support | Best performance |
| Firefox 75+ | ✅ Full Support | Good performance |
| Safari 14+ | ✅ Full Support | WebAssembly SIMD may be limited |
| Edge 80+ | ✅ Full Support | Chromium-based |
| Mobile Safari | ✅ Supported | IndexedDB quota may be limited |
| Mobile Chrome | ✅ Supported | Full feature support |
Examples
Complete Integration Example
See pkg/plaid-demo.html for a complete working example with:
- WASM initialization
- Transaction processing
- Pattern visualization
- Heatmap display
- Sample data loading
- Data export/import
Running the Demo
# Build WASM
./scripts/build-wasm.sh
# Serve the demo
npx serve pkg
# Open http://localhost:3000/plaid-demo.html
Troubleshooting
WASM Won't Load
- Ensure CORS headers allow
application/wasm - Check browser console for specific error
- Verify WASM file is accessible
IndexedDB Errors
- Check browser's storage quota
- Ensure site isn't in private/incognito mode
- Try clearing site data and reinitializing
Learning Not Improving
- Ensure
recordOutcome()is called with correct rewards - Check that transactions have varied categories
- Verify state is being saved (
save()after changes)
License
MIT License - See LICENSE file for details.