Every transaction. Every millisecond. Every threat.
Sentinel is a production-grade, event-driven transaction anomaly scoring engine that scores financial transactions in real time using a dual-engine approach β a 9-rule deterministic engine running in parallel with a live ML microservice β aggregating both into a single verdict, streamed to a live dashboard over WebSockets.
Architecture β’ Rules Engine β’ ML Service β’ API Docs β’ Testing β’ Setup
Watch Sentinel in action!
π Click here to view the Google Drive Demo Video (https://drive.google.com/file/d/1HYvNNlW45PMOqGAbC7aJ-7r429RszniW/view?usp=sharing)
Here is a glimpse of the Sentinel Dashboard in action:
| Feature | Sentinel |
|---|---|
| Processing Model | Fully async, event-driven via Apache Kafka |
| Scoring Engines | 9 rule-based checks + Isolation Forest ML model β run in parallel |
| Weighting | 75% rules Β· 25% ML Β· graceful timeout fallback |
| Velocity Tracking | Redis ZSET sliding windows: 5-min Β· 1-hr Β· 24-hr |
| AML Patterns | Structuring detection Β· Beneficiary/mule analysis Β· 90-day tracking |
| Real-time Output | WebSocket push to live dashboard on every verdict |
| Idempotency | Duplicate requestId silently ignored β safe for retries |
| DB Write Authority | Only Spring Boot writes to the database β ML service is read-only |
graph TB
Client["π₯οΈ Client / Postman"] -->|POST /transaction/check| API["Spring Boot API\n:8080"]
subgraph "Event Bus β Apache Kafka"
T1["π¨ transactions-incoming"]
T2["π¨ engine-input"]
T3["π¨ rule-scores"]
T4["π¨ ml-scores"]
T5["π¨ risk-results"]
end
subgraph "Spring Boot Services"
TS["TransactionService\n(Kafka Producer + Consumer)"]
RSS["RiskScoringService\n(9 Rules Engine)"]
AGG["AggregatorService\n(Score Combiner)"]
BS["BroadcastService\n(WebSocket)"]
end
subgraph "ML Microservice β FastAPI :8001"
FE["FeatureEngineer\n(Redis Feature Extraction)"]
IF["IsolationForest Model\n(17 behavioral features)"]
end
subgraph "Storage"
PG[("π PostgreSQL\nTransactions + Risk")]
RD[("π΄ Redis\nVelocity Β· History Β· Beneficiary")]
end
API --> T1
T1 --> TS
TS -->|Save txn| PG
TS -->|Write velocity| RD
TS -->|Write history| RD
TS -->|Write beneficiary| RD
TS --> T2
T2 --> RSS
T2 --> FE
FE --> IF
RSS --> T3
IF --> T4
T3 --> AGG
T4 --> AGG
AGG -->|Save RiskAssessment| PG
AGG --> T5
T5 --> BS
BS -->|"/dashboard/alerts"| Dashboard["π Live Dashboard\n(WebSocket + STOMP)"]
sequenceDiagram
participant C as Client
participant SB as Spring Boot
participant K as Kafka
participant RE as Rules Engine
participant ML as ML FastAPI
participant R as Redis
participant DB as PostgreSQL
participant WS as WebSocket
C->>SB: POST /transaction/check {requestId, amount, userId...}
SB->>SB: Validate user + merchant exist
SB->>K: Publish β transactions-incoming
SB-->>C: 202 Accepted
K->>SB: consume() [risk-analyzer-group]
SB->>DB: INSERT Transaction (status=UNFLAGGED)
SB->>R: ZADD users:{id}:velocity (5-min/1-hr/24-hr windows)
SB->>R: ZADD history:users:{id} (30-day cache)
SB->>R: ZADD beneficiary:user:{id} (90-day merchant tracking)
SB->>K: Publish β engine-input (TransactionEnrichedDto)
par Parallel Execution
K->>RE: consume() [rules-engine-group]
RE->>DB: Load 30-day tx history
RE->>R: Read velocity ZSETs
RE->>R: Read beneficiary ZSETs
RE->>RE: Run 9 rules β weighted score
RE->>K: Publish β rule-scores
and
K->>ML: consume() [ml-engine-group]
ML->>R: Read velocity / beneficiary features
ML->>ML: Extract 17 features β Isolation Forest
ML->>K: Publish β ml-scores
end
K->>SB: consumeRuleScore() + consumeMlScore()
SB->>SB: Aggregate: 75% rules + 25% ML
SB->>DB: SAVE RiskAssessment
SB->>DB: UPDATE Transaction.status (FLAGGED / UNFLAGGED)
SB->>K: Publish β risk-results
K->>WS: consumeForRiskResult()
WS-->>C: Push RiskAssessmentDto via STOMP /dashboard/alerts
flowchart TD
A["rule-scores received"] --> B{ml-scores\nalready here?}
B -- Yes --> C["Aggregate Immediately\n75% rules + 25% ML"]
B -- No --> D["Schedule 3-sec timeout"]
D --> E{ml-scores arrive\nbefore timeout?}
E -- Yes --> C
E -- No --> F["Fallback: 100% rules score"]
C --> G["Save RiskAssessment β DB"]
G --> H["Publish β risk-results"]
H --> I["WebSocket push to dashboard"]
--
Sentinel/
β
βββ π docker-compose.yml # Zookeeper + Kafka + Redis
βββ π pom.xml # Spring Boot 3.5 deps
β
βββ π ml-service/ # Python FastAPI ML Microservice
β βββ Dockerfile
β βββ requirements.txt
β βββ train_model.py # Isolation Forest training script
β βββ models/
β β βββ fraud_model.pkl # Trained model artifact
β β βββ scaler.pkl # StandardScaler artifact
β β βββ feature_schema.json # 17-feature strict schema
β βββ app/
β βββ main.py # FastAPI app + Kafka consumer/producer
β βββ ml_model.py # IsolationForest wrapper
β βββ feature_engineering.py # Redis-backed feature extraction
β βββ config.py # Pydantic settings
β
βββ β src/
βββ main/
βββ resources/
β βββ application.properties
β βββ static/
β βββ index.html # Built-in Sentinel Dashboard UI
β
βββ java/com/example/Sentinel/
β
βββ SentinelApplication.java
βββ WebConfig.java # CORS config
β
βββ ποΈ config/
β βββ KafkaConfig.java # 5 topics + 5 consumer factories
β βββ RedisConfig.java # StringRedisSerializer setup
β βββ WebsocketConfig.java # STOMP endpoint config
β βββ MccRegistry.java # MCC risk level map
β
βββ π controller/
β βββ TransactionController.java
β βββ UserController.java
β βββ DashboardController.java
β
βββ π¦ dto/
β βββ MoneyTransferDto.java # Inbound request
β βββ TransactionEnrichedDto.java # Internal Kafka msg
β βββ RuleScoreDto.java # Rules engine output
β βββ MlScoreDto.java # ML service output
β βββ RiskAssessmentDto.java # Final verdict
β βββ TransactionDto.java
β βββ UsersDetailDto.java
β
βββ ποΈ entity/
β βββ Transaction.java
β βββ Users.java
β βββ RiskAssessment.java
β
βββ ποΈ repo/
β βββ TransactionRepo.java
β βββ UsersRepo.java
β βββ RiskAssessmentRepo.java
β
βββ βοΈ rules/ # 9 Pluggable Rule Classes
β βββ AmountRule.java # Z-score deviation
β βββ VelocityRule.java # Redis ZSET sliding windows
β βββ UserLocationRule.java # Location frequency %
β βββ TimeOfTransactionRule.java # Hour-of-day pattern
β βββ MerchantCategoryCodeRule.java # MCC risk level
β βββ CrossBorderRule.java # International flag
β βββ DeviceFingerPrintRule.java # Device frequency %
β βββ StructuringRule.java # AML structuring detection
β βββ BeneficiaryRule.java # Mule account detection
β
βββ π§ services/
βββ TransactionService.java # Core orchestrator
βββ RiskScoringService.java # Rules aggregation
βββ AggregatorService.java # ML + Rules combiner
βββ BroadcastService.java # WebSocket broadcaster
Sentinel runs 9 deterministic rules on every transaction. Each rule produces a score; scores are combined using a calibrated weighted formula.
| Rule | Max Score | Signal | Threshold |
|---|---|---|---|
| Amount Rule | 40 | Z-score vs. 30-day history | >3Ο = 40pts |
| Velocity Rule | 35 | Redis ZSET count in windows | >6 txn/5min = 35pts |
| Structuring Rule | 80 | AML near-threshold clustering | 5+ txns 70k-100k in 48hr |
| Merchant Category | 30 | MCC risk level + novelty | Gambling = 30pts |
| Beneficiary Rule | 30 | Mule detection via 90-day ZSET | >7 unique merchants/24hr |
| Time Rule | 25 | Hour-of-day pattern | 2AMβ5AM unseen = 25pts |
| Location Rule | 10 | Location frequency % | <60% familiar = 10pts |
| Device Fingerprint | 10 | Device frequency % | <60% familiar = 10pts |
| Cross-Border | 10 | International flag | Any cross-border = 10pts |
weighted = (amountScore Γ 0.20)
+ (velocityScore Γ 0.15)
+ (locationScore Γ 0.10)
+ (mccScore Γ 0.10)
+ (timeScore Γ 0.10)
+ (crossBorderScore Γ 0.05)
+ (deviceScore Γ 0.10)
+ (structuringScore Γ 0.10)
+ (beneficiaryScore Γ 0.10)
scaledScore = (weighted / 32.25) Γ 100
if nonZeroRules β₯ 5 β scaledScore Γ 1.35 β multi-rule amplifier
if nonZeroRules β₯ 3 β scaledScore Γ 1.20
ruleScore = min(scaledScore, 100)
finalScore = (ruleScore Γ 0.75) + (mlScore Γ 0.25) β both engines present
= ruleScore β ML timed out (graceful)
= mlScore β rules timed out
fraudLevel: LOW (0β35) | MEDIUM (36β50) | HIGH (51β100)
A standalone Python FastAPI service that consumes the engine-input Kafka topic and publishes scores to ml-scores.
Amount Features: amount_zscore, amount_percentile
Velocity Features: velocity_5min, velocity_1hr, velocity_24hr, velocity_burst_score
Merchant Features: merchant_seen_before, unique_merchants_24hr, merchant_diversity_score
Device Features: device_seen_before, device_novelty_score
Location Features: location_seen_before, location_novelty_score
Time Features: hour_of_day, is_unusual_hour, hour_deviation_score
Border Feature: is_cross_border
All features are extracted from live Redis data β no simulation, no stale data.
Algorithm: Isolation Forest (sklearn)
Training Data: 10,000 synthetic samples (90% legit / 10% fraud)
Scaler: StandardScaler
Output: fraud_score (0β100%), confidence, prediction_class
cd ml-service
python train_model.py
# Generates: models/fraud_model.pkl, models/scaler.pkl, models/feature_schema.jsonThree independent ZSET namespaces power real-time risk signals:
| Key Pattern | Purpose | TTL Window |
|---|---|---|
users:{id}:velocity |
Transaction count for velocity rule | 24 hours |
history:users:{id} |
Transaction ID cache for 30-day lookups | 30 days |
beneficiary:user:{id} |
Merchant IDs for mule detection | 90 days |
ZADD users:1:velocity <epoch_ms> <txnId> β 5-min / 1-hr / 24-hr ZCOUNT
ZADD history:users:1 <epoch_ms> <txnId> β rangeByScore for DB fallback
ZADD beneficiary:user:1 <epoch_ms> <merchantId> β unique merchant count
POST /users/add
Content-Type: application/json
{
"email": "alice@example.com",
"phoneNumber": "9876543210",
"name": "Alice Johnson",
"homeLocation": "Mumbai"
}201 Created
"User created..."
409 Conflict
"User already existed"GET /users/userdetails?user_id=1200 OK
{
"userId": 1,
"email": "alice@example.com",
"name": "Alice Johnson",
"phoneNumber": "9876543210",
"homeLocation": "Mumbai",
"createdAt": "2026-03-07T10:00:00"
}POST /transaction/check
Content-Type: application/json
{
"requestId": "txn-001",
"amount": 85000.00,
"locationOfUser": "Mumbai",
"timeOfPayment": "2026-03-07T03:00:00",
"merchantId": 4,
"userId": 1,
"merchantCategoryCode": 7995,
"crossBorder": true,
"deviceFingerPrint": "device-unknown-hacker"
}202 Accepted
"transaction accepted"The response is immediate. The actual fraud score is computed asynchronously and pushed to the WebSocket dashboard.
GET /transaction/details?transactionId=1200 OK
{
"transactionId": 1,
"userId": 1,
"amount": 85000.00,
"merchantId": 4,
"userLocation": "Mumbai",
"timeOfTransaction": "2026-03-07T03:00:00",
"status": "FLAGGED",
"merchantCategoryCode": 7995,
"crossBorder": true,
"deviceFingerPrint": "device-unknown-hacker"
}GET /transaction/top10ForUser?userId=1GET /transaction/Last30ForUser?userId=1GET /dashboard/history200 OK
[
{
"id": 1,
"transactionId": 42,
"userId": 1,
"amount": 85000.00,
"amountScore": 40,
"velocityScore": 35,
"locationScore": 10,
"timeScore": 25,
"merchantCategoryScore": 30,
"crossBorderScore": 10,
"deviceFingerPrintScore": 10,
"structuringScore": 30,
"beneficiaryScore": 15,
"sequenceScore": 0,
"overallScore": 94.7,
"mlScore": 78.3,
"fraudPossibility": "HIGH",
"triggeredRules": [
"Amount Rule",
"Velocity Rule",
"Structuring Rule",
"Cross Border Rule",
"Merchant Category Rule",
"Device Finger Print Rule",
"Time Of Transaction Rule",
"ML Model Alert"
]
}
]GET /dashboard/riskByTransaction?transactionId=42GET http://localhost:8001/health{
"status": "healthy",
"service": "ML Fraud Detection Service",
"model_version": "fraud-detector-v1.0",
"model_type": "Behavioral Anomaly Detection"
}POST http://localhost:8001/predict
Content-Type: application/json
{ ...transaction fields... }{
"fraudScore": 78.3,
"confidence": 0.82,
"predictionClass": "FRAUD",
"modelVersion": "fraud-detector-v1.0",
"extractedFeatures": { ... }
}A curated, production-quality Postman collection is included: Sentinel_Postman_Tests_Version2.json
Import it directly into Postman. Execute folders in order β each section builds state for the next.
π 1. User Management
βββ 1.1 Create User - Alice (Valid)
βββ 1.2 Create User - Duplicate Email β expects 409
βββ 1.3a Create User - Bob
βββ 1.3b Create User - Merchant 1 (Electronics)
βββ 1.3c Create User - Merchant 2 (Gambling)
βββ 1.3d Create User - Merchant 3 (Grocery)
βββ 1.4 Get User Details - Valid ID 1
βββ 1.5 Get User Details - Invalid ID 999 β expects 400
βββ 1.6 Create User - Invalid Email β expects 400
βββ 1.7 Create User - Invalid Phone β expects 400
π 2. Basic Transaction Flow
βββ 2.1 Normal Transaction - Low Risk
βββ 2.1b Verify Transaction Details (ID=1)
βββ 2.2 Duplicate RequestId - Idempotency β silent no-op
βββ 2.3 Transaction - Non-existent User β expects 500
βββ 2.4 Transaction - Non-existent Merchant β expects 500
π 3. Amount Rule Testing
βββ 3.1aβe Build Baseline (1000, 1200, 900, 1100, 1050)
βββ 3.2 Moderate Amount (1Οβ2Ο) β score 10
βββ 3.3 High Amount (2Οβ3Ο) β score 25
βββ 3.4 Very High Amount (>3Ο) β score 40
π 4. Velocity Rule Testing
βββ 4.1 Normal Velocity Baseline
βββ 4.2aβe 5-min Velocity (1β5 txns) β score 0
βββ 4.3 6th txn in 5 min β score 15
βββ 4.3b 7th txn in 5 min β score 35
π 5. Location Rule Testing
βββ 5.1aβj Build Mumbai baseline (10 consistent txns)
βββ 5.2 Moderate - Delhi β score 5
βββ 5.2b Moderate - Delhi (2nd) β score 5
βββ 5.3 Suspicious - Kolkata β score 10
π 6. Time Rule Testing
βββ 6.1 Normal Business Hours (2PM) β score 0
βββ 6.2 Unusual Hours (3AM) β score 25
βββ 6.3 Off-hours (8PM) β score 15
π 7. MCC Rule Testing
βββ 7.1 Low-Risk MCC (Grocery 5411) β score 5
βββ 7.2 Medium-Risk MCC (Travel 4722) β score 15
βββ 7.3 High-Risk MCC (Gambling 7995) β score 30
βββ 7.4 Unknown MCC (9999) β score 10
π 8. Cross-Border Rule Testing
βββ 8.1 Domestic Transaction β score 0
βββ 8.2 Cross-Border β score 10
π 9. Device Fingerprint Rule Testing
βββ 9.1aβj Consistent Device (10 txns with same fingerprint) β score 0
βββ 9.2 Moderate - New Phone β score 5
βββ 9.3 Suspicious Unknown Device β score 10
π 10. Structuring Rule Testing (AML)
βββ 10.1 Normal single txn (50k) β no flag
βββ 10.2aβe Rapid Near-Threshold 5 txns (75kβ85k in 2hr) β score 30
βββ 10.3 Cumulative >10L (650k) β score 50+
π 11. Beneficiary Rule Testing (Mule Detection)
βββ 11.1 Normal - Existing Merchant β score 0β5
βββ 11.2aβb 24-hr Merchant Velocity β score 5β10
βββ 11.3a High Value to New Beneficiary 1 (25k) β score elevated
βββ 11.3b High Value to New Beneficiary 2 (30k) β score elevated
# 1. Open Postman
# 2. Click Import β Upload File β select Sentinel_Postman_Tests_Version2.json
# 3. Set environment variable: baseUrl = http://localhost:8080
# 4. Run folders in sequence 1 β 11| Feature | What it does | |
|---|---|---|
| β‘ | Dual-Engine Scoring | 9 rules + Isolation Forest ML run in parallel. Weighted 75/25, with 3-sec graceful fallback if either engine lags. |
| π | Fully Async Pipeline | API returns 202 Accepted instantly. All scoring flows through 5 Kafka topics β zero blocking. |
| π΄ | Redis Velocity Windows | ZSET sliding windows at 5-min Β· 1-hr Β· 24-hr per user. Self-pruning on every write β sub-millisecond reads. |
| π¦ | AML Structuring Detection | Flags 5+ near-threshold txns (βΉ70kββΉ1L) within 48hrs, or cumulative volume breaching βΉ10L. |
| π΅οΈ | Mule Account Detection | 90-day rolling ZSET tracks unique merchant relationships. Rapid fan-out to new merchants = red flag. |
| π | Idempotent Processing | requestId deduplicates at the DB constraint level β atomic, race-safe, zero extra round trips. |
| π‘ | Live WebSocket Dashboard | Every verdict pushed via STOMP/SockJS. Built-in /index.html β no frontend build needed. |
| π‘οΈ | Single Write Authority | Only Spring Boot writes to PostgreSQL. ML service is purely stateless β reads Redis, writes Kafka. |
| Capability | Detail | |
|---|---|---|
| π¨ | Live Alert Feed | Colour-coded π΄π‘π’ verdicts streaming in real time with triggered rule badges and ML score |
| π | Risk Distribution Chart | Chart.js doughnut β auto-updates on every WebSocket push |
| π | Transaction Search | By txn ID Β· top-10 per user Β· 30-day history β all enriched with risk scores |
| π€ | User Lookup | Instant profile fetch β name, email, location, registration date |
| π | Live Stats Cards | Total transactions Β· High-risk count Β· Avg risk score Β· Flagged rate |
| π | History Restore | Pulls full DB history on page load β dashboard never starts empty |
| Optimization | Impact |
|---|---|
| Redis-First Reads | ZSETs serve velocity + history + beneficiary at O(log N). PostgreSQL only hit on cold cache. Rule eval stays under 5ms warm. |
| True Parallel Engines | Rules engine and ML service are separate consumer groups on engine-input. Neither blocks the other β aggregator merges on first-arrival. |
| Self-Pruning ZSETs | ZREMRANGEBYSCORE runs on every write β no TTL jobs, no memory bloat, windows stay exact. |
| Typed Kafka Factories | 5 dedicated ConcurrentKafkaListenerContainerFactory instances β deserialization errors in one topic can't contaminate others. |
| Lazy Loading + EntityGraph | FetchType.LAZY everywhere. @EntityGraph applied surgically only where joins are genuinely needed β eliminates N+1. |
| DB-Level Idempotency | Unique constraint on requestId β atomic duplicate rejection with no extra SELECT or Redis lock. |
| ConcurrentHashMap Aggregation | Pending rule/ML scores held in memory β zero DB reads during the aggregation window, cleaned up immediately after publish. |
- Kafka partition-by-userId β Guarantee per-user ordering without global locks
- Redis Cluster sharding β Distribute ZSETs across nodes for million-user scale
- ONNX model serving β 3β5Γ faster ML inference, language-agnostic portability
- PostgreSQL read replicas β Offload dashboard history reads from the primary write node
| Service | Platform | Free Tier |
|---|---|---|
| Spring Boot API | Back4App Containers | β Yes |
| ML FastAPI Service | Back4App Containers | β Yes |
| Dashboard Frontend | Netlify | β Yes |
| PostgreSQL | Back4App (built-in) | β Yes |
| Redis | Redis Labs (Redis Cloud) | β 30MB free |
| Apache Kafka | Upstash Kafka | β 10k msg/day free |
1. Go to https://redis.com/try-free/
2. Create a free database (30MB, no credit card)
3. Copy: Public Endpoint, Password
4. Update application.properties:
spring.data.redis.host=redis-XXXXX.c1.us-east-1-2.ec2.cloud.redislabs.com
spring.data.redis.port=XXXXX
spring.data.redis.password=YOUR_REDIS_PASSWORD# ml-service/app/feature_engineering.py
self.redis_client = redis.Redis(
host="redis-XXXXX.c1.us-east-1-2.ec2.cloud.redislabs.com",
port=XXXXX,
password="YOUR_REDIS_PASSWORD",
decode_responses=True,
ssl=True
)1. Go to https://upstash.com/ β Create Kafka Cluster
2. Create 5 topics manually:
transactions-incoming Β· engine-input Β· rule-scores Β· ml-scores Β· risk-results
3. Copy: Bootstrap Server URL, Username, Password
4. Update application.properties:
spring.kafka.bootstrap-servers=YOUR-CLUSTER.upstash.io:9092
spring.kafka.properties.security.protocol=SASL_SSL
spring.kafka.properties.sasl.mechanism=SCRAM-SHA-256
spring.kafka.properties.sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required \
username="YOUR_USERNAME" password="YOUR_PASSWORD";# ml-service/app/config.py β update kafka_bootstrap_servers
# Add SASL config to KafkaConsumer and KafkaProducer in main.py:
security_protocol="SASL_SSL",
sasl_mechanism="SCRAM-SHA-256",
sasl_plain_username="YOUR_USERNAME",
sasl_plain_password="YOUR_PASSWORD"1. Train model locally first: python train_model.py
(Commit fraud_model.pkl + scaler.pkl + feature_schema.json to the repo)
2. Go to https://www.back4app.com/ β Containers β New Container
3. Connect your GitHub repo, select the ml-service/ folder
4. Set root directory: ml-service
5. Dockerfile is already present β Back4App auto-detects it
6. Add Environment Variables:
KAFKA_BOOTSTRAP_SERVERS=YOUR-CLUSTER.upstash.io:9092
REDIS_HOST=redis-XXXXX.c1.us-east-1-2.ec2.cloud.redislabs.com
REDIS_PORT=XXXXX
REDIS_PASSWORD=YOUR_REDIS_PASSWORD7. Deploy β Copy the public URL (e.g. https://ml-service-xxx.b4a.run)
1. Create a Dockerfile in the project root:
FROM eclipse-temurin:17-jdk-alpine AS build
WORKDIR /app
COPY . .
RUN ./mvnw package -DskipTests
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]2. Go to Back4App β Containers β New Container
3. Connect GitHub repo (root directory = project root)
4. Add Environment Variables:
SPRING_DATASOURCE_URL=jdbc:postgresql://YOUR_PG_HOST:5432/sentinel
SPRING_DATASOURCE_USERNAME=postgres
SPRING_DATASOURCE_PASSWORD=YOUR_PG_PASSWORD
SPRING_DATA_REDIS_HOST=redis-XXXXX.c1.us-east-1-2.ec2.cloud.redislabs.com
SPRING_DATA_REDIS_PORT=XXXXX
SPRING_DATA_REDIS_PASSWORD=YOUR_REDIS_PASSWORD
SPRING_KAFKA_BOOTSTRAP_SERVERS=YOUR-CLUSTER.upstash.io:9092
CORS_ORIGINS=https://your-frontend.netlify.app5. Deploy β Copy the public URL (e.g. https://sentinel-xxx.b4a.run)
The dashboard is a single index.html in src/main/resources/static/. Deploy it independently:
1. Copy src/main/resources/static/index.html to a new folder: sentinel-dashboard/
2. Update the API_BASE constant in index.html:
// Line ~230 in index.html
const API_BASE = 'https://sentinel-xxx.b4a.run'; // Your Back4App URL3. Go to https://netlify.com β Sites β Deploy manually
4. Drag-and-drop the sentinel-dashboard/ folder
5. Done β your dashboard is live at https://your-site.netlify.app
β Redis Cloud endpoint + password updated in both Spring Boot and ML service
β Upstash Kafka SASL credentials set in both services
β All 5 Kafka topics created in Upstash dashboard
β ML model artifacts committed (fraud_model.pkl, scaler.pkl, feature_schema.json)
β CORS_ORIGINS set to your Netlify domain in Back4App env vars
β API_BASE in index.html updated to Back4App Spring Boot URL
β spring.jpa.hibernate.ddl-auto=update (not create-drop) for prod
β H2 console disabled: spring.h2.console.enabled=false
Java 17+
Maven 3.9+
Docker + Docker Compose
Python 3.11+
docker-compose up -d
# Starts: Zookeeper (:2181) Β· Kafka (:9092) Β· Redis (:6379)cd ml-service
pip install -r requirements.txt
python train_model.py
# Generates fraud_model.pkl + scaler.pkl + feature_schema.jsonuvicorn app.main:app --host 0.0.0.0 --port 8001./mvnw spring-boot:run
# API available at http://localhost:8080
# Dashboard at http://localhost:8080/index.html
# H2 console at http://localhost:8080/h2-consoleImport Sentinel_Postman_Tests_Version2.json into Postman and execute all folders in order.
| Technology | Version | Role |
|---|---|---|
| Java | 17 | Language |
| Spring Boot | 3.5 | Framework |
| Spring Kafka | Latest | Event streaming |
| Spring Data JPA | Latest | ORM |
| Spring Data Redis | Latest | Redis client |
| Spring WebSocket | Latest | Real-time push |
| H2 | Runtime | Dev in-memory DB |
| PostgreSQL | Latest | Prod DB |
| Jakarta Validation | Latest | Input validation |
| Technology | Version | Role |
|---|---|---|
| FastAPI | 0.109 | REST API + Kafka service host |
| Uvicorn | 0.27 | ASGI server |
| scikit-learn | 1.4 | Isolation Forest model |
| kafka-python | 2.0.2 | Kafka consumer + producer |
| Redis-py | 5.0.1 | Live feature extraction |
| NumPy | 1.26 | Numerical computation |
| Pandas | 2.1 | Synthetic training data |
| Joblib | 1.3 | Model + scaler serialization |
| Pydantic | 2.5 | Schema validation |
| pydantic-settings | 2.1 | Config via env vars |
| python-dotenv | 1.0 | .env file loading |
| Service | Image | Port |
|---|---|---|
| Apache Kafka | confluentinc/cp-kafka:7.2.1 | 9092 |
| Zookeeper | confluentinc/cp-zookeeper:7.2.1 | 2181 |
| Redis | redis:7-alpine | 6379 |
| Topic | Partitions | Producer | Consumer | Payload |
|---|---|---|---|---|
transactions-incoming |
3 | TransactionController | TransactionService | MoneyTransferDto |
engine-input |
1 | TransactionService | RulesEngine + ML | TransactionEnrichedDto |
rule-scores |
3 | RulesEngine | AggregatorService | RuleScoreDto |
ml-scores |
3 | ML FastAPI | AggregatorService | MlScoreDto |
risk-results |
3 | AggregatorService | BroadcastService | RiskAssessmentDto |
| Overall Score | Fraud Level | Transaction Status |
|---|---|---|
| 0 β 35 | π’ LOW | UNFLAGGED |
| 36 β 50 | π‘ MEDIUM | UNFLAGGED |
| 51 β 100 | π΄ HIGH | FLAGGED |
| MCC Code | Category | Risk Level |
|---|---|---|
| 7995 | Gambling | HIGH |
| 6010, 6011 | Cash Advance / ATM | HIGH |
| 6051 | Crypto / Non-bank | HIGH |
| 4829 | Wire Transfer | HIGH |
| 5732, 5944 | Electronics / Jewelry | MEDIUM |
| 4722, 7011 | Travel / Hotels | MEDIUM |
| 5411, 5912 | Grocery / Pharmacy | LOW |
| 5814, 4111 | Food / Transit | LOW |
All inbound MoneyTransferDto fields are validated at the controller layer:
amount β @Positive, @NotNull
locationOfUser β @NotBlank
timeOfPayment β @PastOrPresent (no future timestamps)
merchantId β @NotNull + must exist in DB
userId β @NotNull + must exist in DB
merchantCategoryCode β @NotNull
crossBorder β @NotNull
deviceFingerPrint β @NotBlank
requestId β @NotBlank (idempotency key)
- PostgreSQL for Prod β Replace H2 with persistent PostgreSQL; switch
ddl-autotoupdate - Auto ML Retraining β Nightly pipeline retrains Isolation Forest on newly flagged transactions
- Sequence Rule β Score unnatural transaction ordering patterns (rapid round-trips, fan-out)
- Rate Limiting β Per-user API throttling via Bucket4j to block transaction flooding
- Alert Workflow β Acknowledge / escalate / dismiss alerts directly from the dashboard
- Grafana Metrics β Kafka consumer lag, rule hit rates, ML latency via Prometheus + Grafana
- SHAP Explainability β Surface top contributing features per ML prediction on the dashboard
Distributed under the MIT License. See LICENSE for more information.