trust-ai
v1.0.2
Published
AI-powered fraud detection, risk assessment & account takeover prevention framework powered by Ollama + MiniMax
Maintainers
Readme
trust-ai
AI-powered fraud detection, risk assessment, and account takeover (ATO) prevention framework for Node.js. Powered by Ollama Cloud + MiniMax.
npm install trust-aiRequires Node.js ≥ 18 · Zero runtime dependencies · CommonJS · Works offline (mock mode)
How it works
Your app → trust(context)
│
├─ 1. Load user profile from JSON database
├─ 2. Rule engine scores 25 signals across 6 categories ← deterministic, no AI
├─ 3. Severity + decision + recommendations computed ← pure rule logic
├─ 4. AI writes a human-readable explanation ← only AI call
└─ 5. Event indexed in vector DB for chatbot / dashboard RAGRules decide everything. AI only writes the explanation text — it does not change the score, severity, or decision.
Quick start
const trust = require('trust-ai');
// Initialize once with your Ollama cloud API key
await trust.AI('your-ollama-api-key');
// Evaluate a login event
const result = await trust({
identity: {
userId: 'user_123',
accountAgeDays: 365,
kycVerified: true,
},
device: {
deviceId: 'iphone15_abc',
os: 'iOS 18',
browser: 'Safari',
isNewDevice: false,
},
location: {
city: 'Mumbai',
country: 'IN',
ipAddress: '117.201.88.14',
},
authentication: {
method: 'PASSWORD',
failedLogins: 0,
mfaEnabled: true,
otpPassed: true,
},
behavior: {
eventType: 'LOGIN',
loginHour: 10,
typingSpeed: 72,
},
});
console.log(result.riskScore); // 5
console.log(result.decision); // "ALLOW"
console.log(result.explanation); // AI-written reasonInitialization
await trust.AI(apiKey, options?)| Option | Type | Default | Description |
|---|---|---|---|
| apiKey | string | — | Your Ollama cloud API key (required) |
| options.model | string | "minimax-m3" | Model to use |
| options.mockMode | boolean | false | Skip API calls, use built-in fallback responses |
Environment variables (alternative to hardcoding):
OLLAMA_API_KEY=your-key
OLLAMA_MODEL=minimax-m3 # optionaltrust(context)
Evaluates one user event and returns a RiskResult.
TrustContext
{
identity: {
userId: string // required
accountAgeDays?: number
accountType?: string
kycVerified?: boolean
}
device: {
deviceId: string // required
fingerprint?: string
os?: string
browser?: string
deviceType?: string
isNewDevice?: boolean
lastSeen?: Date
}
location: {
city?: string
state?: string
country?: string // ISO 3166-1 alpha-2 (e.g. "IN", "US")
latitude?: number
longitude?: number
ipAddress?: string
}
authentication: {
method?: 'PASSWORD' | 'OTP' | 'BIOMETRIC' | 'SOCIAL' | 'PIN'
otpPassed?: boolean
failedLogins?: number
passwordReset?: boolean
accountRecovery?: boolean
emailChanged?: boolean
phoneChanged?: boolean
mfaEnabled?: boolean
}
behavior: {
eventType: 'LOGIN' | 'TRANSACTION' | 'PROFILE_UPDATE' | 'PASSWORD_RESET' | 'LOGOUT'
loginHour?: number // 0–23
sessionDuration?: number // seconds
typingSpeed?: number // WPM
mouseMovements?: number
clickPattern?: string
}
transaction?: { // include only for TRANSACTION events
amount?: number
currency?: string
type?: 'TRANSFER' | 'PAYMENT' | 'WITHDRAWAL' | 'DEPOSIT'
beneficiaryNew?: boolean
frequency?: number
}
metadata?: {
channel?: 'MOBILE_APP' | 'WEB' | 'ATM' | 'BRANCH' | 'API'
userAgent?: string
timestamp?: Date
sessionId?: string
}
}RiskResult
{
riskScore: number // 0–100
trustScore: number // 100 - riskScore
severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'
decision: 'ALLOW' | 'CHALLENGE' | 'REVIEW' | 'BLOCK'
reasons: string[] // list of triggered rules
explanation: string // AI-written human explanation
recommendations: string[] // rule-based action list
timestamp: Date
incidentId?: string // set for MEDIUM and above (e.g. "INC-20260619-A3F2")
}Score ranges
| Score | Severity | Decision | Meaning | |---|---|---|---| | 0–25 | LOW | ALLOW | Clean session, grant access | | 26–50 | MEDIUM | CHALLENGE | Anomaly detected, send OTP | | 51–75 | HIGH | REVIEW | Multiple signals, hold for review | | 76–100 | CRITICAL | BLOCK | Clear attack pattern, terminate session |
Rule engine
25 rules across 6 categories. All deterministic — same inputs always produce the same score.
Scoring formula
finalScore = softCap( base + Σ capped(category) )- base — 10 for new users (no baseline), 5 for returning users
- capped(category) — each category has an independent ceiling to prevent one dimension from dominating
- softCap — diminishing returns above 85; prevents artificial 100s
Categories and weights
| Category | Rules | Category cap | |---|---|---| | Device | New device (+18), device changed (+12) | 22 | | Location | New country (+24), new city (+8), impossible travel (+28), new IP subnet (+6) | 30 | | Authentication | 1–2 failures (+7 each), ≥3 failures (+22), OTP failed (+13), password reset (+16), account recovery (+23), phone changed (+20), email changed (+20), no MFA (+7) | 38 | | Behavior | Midnight hour 0–6 AM (+10), off-pattern hour (+5), bot typing >250 WPM (+12), no mouse on web (+8), short session <30s (+4) | 18 | | Transaction | Amount >5× avg (+15), >10× avg (+22), new beneficiary (+10), high frequency (+12), round number (+4) | 22 | | Velocity | 3–4 events/hr (+10), 5–9 (+15), 10+ (+18) | 18 |
Example score outcomes
| Scenario | Score | Decision | |---|---|---| | Clean first login | ~10 | ALLOW | | Clean returning login | ~5 | ALLOW | | New country only | ~34 | CHALLENGE | | New device + 2 failed logins | ~46 | CHALLENGE | | New device + new city + 2 failures | ~56 | REVIEW | | Full attack (10+ signals) | ~91 | BLOCK |
Audit logs
// All events for a user
const logs = trust.provideLog('user_123');
// Filtered
const logs = trust.provideLog('user_123', {
severity: 'CRITICAL',
eventType: 'LOGIN',
since: new Date('2026-01-01'),
limit: 50,
});
// All CRITICAL events across all users
const logs = trust.provideLog(undefined, { severity: 'CRITICAL' });Each LogEntry:
{
id: string
userId: string
timestamp: Date
eventType: string
riskScore: number
severity: Severity
decision: Decision
reasons: string[]
explanation: string
incidentId: string
}Client chatbot
A user-facing assistant that answers questions about blocked sessions, security alerts, or account safety. Uses RAG over the user's own event history to give contextual answers.
const res = await trust.chatbot.chat(
'Why was my login blocked?',
'user_123' // optional — scopes RAG to this user's events
);
console.log(res.message); // jargon-free explanation
console.log(res.confidence); // 0–1
console.log(res.sources); // relevant past events used as contextDesign goal: The chatbot is intentionally reassuring. It explains what happened and what the user should do next — without revealing internal scoring details.
Bank dashboard
An AI analyst for fraud operations staff. Answers free-text questions about users, incidents, or system-wide patterns using RAG over all event history plus live database stats.
// Ask about a specific user
const res = await trust.dashboard.query(
'Is user_123 compromised and what should we do?',
{ userId: 'user_123' }
);
console.log(res.answer); // detailed AI analysis
console.log(res.actions); // recommended steps for the analyst
// System-wide question
const res = await trust.dashboard.query(
'How many CRITICAL events happened today and which users are affected?'
);
// Get raw stats for dashboard widgets
const stats = await trust.dashboard.getStats();
// { totalUsers, totalEvents, criticalEvents, highEvents, ... }Memory system
All data is stored as local JSON files. No external database required.
data/
users.json ← user profiles and behavioral baselines
logs.json ← full audit event log
vectors.json ← RAG vector index for chatbot/dashboardEach store has a 5 MB cap. When exceeded, the oldest data is automatically pruned:
users.json— drops users not seen in 90 days, trimsrecentEventsto 50 per userlogs.json— drops oldest log entriesvectors.json— keeps the 1000 most recent documents
const mem = trust.memory.stats();
// { users: 2800, logs: 6700, vectors: 86400, totalMB: '0.09' }Demo files
# Full demo — two logins for one user, chatbot + dashboard queries
node demo/demo.js
# Persistence test — run in order
node demo/demo1.js # first login → saves baseline
node demo/demo2.js # second login → loads baseline, computes risk deltademo2.js prints the stored profile at Step 1 before running the second evaluation, so you can see exactly what the engine is comparing against.
Project structure
trust-ai/
├── src/
│ ├── index.js ← main entry point / public API
│ ├── types/
│ │ └── index.d.ts ← TypeScript types
│ └── core/
│ ├── ai-client.js ← Ollama cloud API client (MiniMax)
│ ├── rule-engine.js ← 25-rule scoring engine (no AI)
│ ├── analyzer.js ← pipeline orchestrator
│ ├── database.js ← user profile store (users.json)
│ └── memory-manager.js ← JSON I/O + 5MB auto-pruner
│ └── features/
│ ├── logger.js ← audit log (logs.json)
│ ├── chatbot.js ← client-facing TrustBot
│ ├── dashboard.js ← bank analyst dashboard AI
│ └── vector-db.js ← local cosine-similarity vector search
├── data/ ← auto-created, gitignored
│ ├── users.json
│ ├── logs.json
│ └── vectors.json
└── demo/
├── demo.js ← full demo
├── demo1.js ← baseline registration
└── demo2.js ← risk check against baselineAI architecture
| Task | Who does it | |---|---| | Risk score | Rule engine (deterministic) | | Severity (LOW/MEDIUM/HIGH/CRITICAL) | Rule engine (thresholds) | | Decision (ALLOW/CHALLENGE/REVIEW/BLOCK) | Rule engine (severity map) | | Recommendations | Rule engine (flag-based logic) | | Human-readable explanation | AI (MiniMax via Ollama) | | Customer chatbot responses | AI (MiniMax via Ollama) | | Dashboard analyst responses | AI (MiniMax via Ollama) | | Vector embeddings | Local sinusoidal hash (no API) |
The rule engine is the source of truth. The AI receives the final verdict and is instructed only to explain — it cannot alter the score or decision.
TypeScript
The package ships with full type definitions. Import normally in TypeScript projects:
import trust from 'trust-ai';
import type { TrustContext, RiskResult } from 'trust-ai';
await trust.AI(process.env.OLLAMA_API_KEY!);
const result: RiskResult = await trust({ ... } as TrustContext);Requirements
- Node.js ≥ 18 (for native
fetchandcrypto) - Ollama cloud account — get your API key at ollama.com
- Zero npm dependencies — all features use Node.js built-ins only
License
MIT
