fraud-intel-service — Local Dev Setup
Version: 1.0 Status: Draft Owner: Trust and Safety + ML Ops + DevEx Last Updated: 2026-04-21 References: SERVICE_OVERVIEW.md, DEPLOYMENT_TOPOLOGY.md, AI_INTEGRATION.md
Local development recipe for fraud-intel-service. The service has two meaningful halves: the online inference/signal service, and the offline ML training pipeline. This doc covers both.
1. Prerequisites
| Tool | Version | Notes |
|---|---|---|
| Node.js | 20 LTS | nvm |
| pnpm | 9.x | corepack enable |
| Python | 3.11 | Training pipeline |
| Docker Engine | 24+ | Compose v2 |
grpcurl | latest | gRPC testing |
psql | 16 | Postgres client |
redis-cli | 7 | Redis client |
nats CLI | 0.1.5+ | NATS testing |
docker-compose-nvidia-gpu | optional | GPU inference in dev (can skip with CPU fallback) |
jq | latest | — |
2. Repository Layout
Ghasi-SMS-Gateway/
services/fraud-intel-service/ ← this service spec
(application repo)/services/fraud-intel-service/ ← code
(application repo)/ml/fraud-intel/ ← Python training pipeline + notebooks
infra/docker/docker-compose.fraud-intel.yml
infra/docker/triton/ ← Triton config + sample models
3. Docker Compose Recipe
Create infra/docker/docker-compose.fraud-intel.yml:
version: '3.9'
services:
postgres-fraud:
image: postgres:16-alpine
environment:
POSTGRES_DB: fraud
POSTGRES_USER: fraud
POSTGRES_PASSWORD: fraud
ports: ["5445:5432"]
volumes:
- fraud_pg_data:/var/lib/postgresql/data
- ./fraud-schema.sql:/docker-entrypoint-initdb.d/001-schema.sql:ro
- ./fraud-seed.sql:/docker-entrypoint-initdb.d/002-seed.sql:ro
redis-fraud:
image: redis:7-alpine
ports: ["6395:6379"]
command: redis-server --appendonly yes
nats:
image: nats:2.10-alpine
command: -js -m 8222
ports: ["4223:4222", "8223:8222"]
minio-fraud:
image: minio/minio:latest
ports: ["9100:9000", "9101:9001"]
environment:
MINIO_ROOT_USER: fraudminio
MINIO_ROOT_PASSWORD: fraudminio
command: server /data --console-address ":9001"
volumes: [fraud_minio_data:/data]
triton-dev:
# For CPU dev; prod is GPU
image: nvcr.io/nvidia/tritonserver:24.03-py3
command: tritonserver --model-repository=/models --strict-model-config=false
ports: ["8000:8000", "8001:8001", "8002:8002"]
volumes:
- ./triton/models:/models:ro
# For real GPU dev, uncomment:
# deploy:
# resources:
# reservations:
# devices:
# - driver: nvidia
# count: 1
# capabilities: [gpu]
mock-misp:
image: ghasi/mock-misp:dev
ports: ["7300:7300"]
volumes:
- ./sample-misp-feed.json:/data/feed.json:ro
fraud-intel:
image: ghasi/fraud-intel-service:dev
depends_on: [postgres-fraud, redis-fraud, nats, minio-fraud, triton-dev, mock-misp]
ports: ["3061:3061", "50061:50061"]
environment:
NODE_ENV: development
LOG_LEVEL: debug
DATABASE_URL: postgres://fraud:fraud@postgres-fraud:5432/fraud
REDIS_URL: redis://redis-fraud:6379/0
NATS_URL: nats://nats:4222
GRPC_PORT: 50061
HTTP_PORT: 3061
TRITON_URL: http://triton-dev:8000
TRITON_MODEL_AIT: ait-detector-v1
TRITON_MODEL_SIMBOX: simbox-detector-v1
TRITON_MODEL_OTP_HARVEST: otp-harvest-detector-v1
ML_ENABLED: "true"
MISP_FEED_URL: http://mock-misp:7300/feed
OBJECT_STORE_ENDPOINT: http://minio-fraud:9000
OBJECT_STORE_KEY: fraudminio
OBJECT_STORE_SECRET: fraudminio
volumes:
fraud_pg_data:
fraud_minio_data:
Start:
docker compose -f infra/docker/docker-compose.fraud-intel.yml up -d
Verify:
curl -s http://localhost:3061/health/ready | jq
curl -s http://localhost:8000/v2/health/ready # Triton
grpcurl -plaintext localhost:50061 list
4. Sample Models for Triton
infra/docker/triton/models/:
triton/models/
ait-detector-v1/
config.pbtxt
1/model.onnx (sample ONNX GraphSAGE pre-trained on synthetic data)
simbox-detector-v1/
config.pbtxt
1/model.onnx (sample isolation-forest as ONNX)
otp-harvest-detector-v1/
config.pbtxt
1/model.onnx (sample LightGBM as ONNX)
Each config.pbtxt configures input/output tensor shapes. Sample:
name: "ait-detector-v1"
platform: "onnxruntime_onnx"
max_batch_size: 64
input [
{
name: "features"
data_type: TYPE_FP32
dims: [42] # 42 engineered features
}
]
output [
{
name: "fraud_score"
data_type: TYPE_FP32
dims: [1]
},
{
name: "confidence"
data_type: TYPE_FP32
dims: [1]
}
]
instance_group [
{ count: 1, kind: KIND_CPU }
]
For dev, the sample models always return fraud_score ∈ [0,1] deterministically from hashed features — predictable for tests.
5. Seed Data
fraud-seed.sql:
-- Sample fraud feeds
INSERT INTO fraud.feeds (id, name, url, schema, active) VALUES
('feed_misp_mock', 'Mock MISP feed', 'http://mock-misp:7300/feed', 'STIX_2_1', true);
-- Sample models registered (matching Triton config)
INSERT INTO fraud.models (id, name, version, serving_name, status, deployed_at, artifact_uri) VALUES
('model_ait_v1', 'AIT detector', '1.0', 'ait-detector-v1', 'PRODUCTION', now(), 's3://fraud-models/ait/v1/'),
('model_simbox_v1', 'SIM-box detector', '1.0', 'simbox-detector-v1', 'PRODUCTION', now(), 's3://fraud-models/simbox/v1/'),
('model_otp_v1', 'OTP harvest detector', '1.0', 'otp-harvest-detector-v1', 'PRODUCTION', now(), 's3://fraud-models/otp/v1/');
-- Seed a few known bad MSISDNs
INSERT INTO fraud.signals (id, kind, msisdn_hash, sender_id, ts, confidence, source) VALUES
('sig_seed_001', 'AIT', encode(sha256('+93700000000'), 'hex'), 'SPAMID', now() - interval '1 hour', 0.95, 'seed'),
('sig_seed_002', 'SIMBOX_INBOUND', encode(sha256('+93701111111'), 'hex'), NULL, now() - interval '2 hour', 0.88, 'seed');
6. Common Commands
6.1 Score an MSISDN
grpcurl -plaintext -d '{
"msisdn": "+93701234567",
"senderId": "BANKOFAF",
"tenantId": "tnt_demo",
"contentFingerprint": "abc123",
"destinationPrefix": "+93"
}' localhost:50061 ghasi.fraud.v1.FraudIntelService/Score
Expected:
{
"fraudScore": 0.12,
"category": "NONE",
"confidence": 0.91,
"features": { "ait_score": 0.05, "simbox_score": 0.03, "otp_harvest_score": 0.15 },
"modelVersions": { "ait": "1.0", "simbox": "1.0", "otp": "1.0" }
}
6.2 Simulate DLR signal stream
for i in {1..100}; do
nats pub sms.dlr.inbound "{\"messageId\":\"msg_${i}\",\"msisdn\":\"+9370000${RANDOM}\",\"status\":\"DELIVERED\",\"submittedAt\":\"2026-04-21T08:00:00Z\"}"
done
6.3 Trigger a feed sync
curl -X POST http://localhost:3061/v1/internal/feeds/feed_misp_mock/sync
6.4 Retroactive scan
curl -X POST http://localhost:3061/v1/internal/scans/retroactive \
-H "Content-Type: application/json" \
-d '{"from": "2026-04-20T00:00:00Z", "to": "2026-04-21T00:00:00Z"}'
6.5 Feedback (T&S correction)
curl -X POST http://localhost:3061/v1/internal/feedback \
-H "Content-Type: application/json" \
-d '{"signalId": "sig_seed_001", "correctedCategory": "NONE", "reason": "Legitimate bulk sender", "reviewer": "tns_alice"}'
6.6 Run training (Python pipeline)
cd ../Ghasi-edTech-equivalent/ml/fraud-intel
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
python train.py \
--model-type ait \
--training-window 30d \
--output-uri s3://fraud-models/ait/v2-candidate/
6.7 Evaluate a candidate model
python evaluate.py \
--candidate s3://fraud-models/ait/v2-candidate/ \
--production s3://fraud-models/ait/v1/ \
--test-set s3://fraud-test-sets/ait-held-out.parquet \
--report evaluation-report.json
6.8 Run tests
pnpm --filter @ghasi/fraud-intel-service test
pnpm --filter @ghasi/fraud-intel-service test:integration
pnpm --filter @ghasi/fraud-intel-service test:contract
pnpm --filter @ghasi/fraud-intel-service test:load:local
6.9 Reset
docker compose -f infra/docker/docker-compose.fraud-intel.yml down -v
7. Environment Variables Reference
| Variable | Default (dev) | Purpose |
|---|---|---|
NODE_ENV | development | — |
LOG_LEVEL | debug | Pino level |
DATABASE_URL | postgres://... | Fraud schema |
REDIS_URL | redis://... | Score cache + feature LRU |
NATS_URL | nats://nats:4222 | Event bus |
GRPC_PORT | 50061 | — |
HTTP_PORT | 3061 | Admin + health |
TRITON_URL | http://triton-dev:8000 | Model serving |
TRITON_MODEL_* | — | Per-category model name |
ML_ENABLED | true | Toggle to disable ML and force rule-based |
MISP_FEED_URL | mock | External feed |
OBJECT_STORE_ENDPOINT | minio | Model artifact + training data |
FEEDBACK_ENABLED | true | T&S correction API |
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Score returns zero fraud consistently | Model not loaded | Check Triton /v2/models/ — ensure all 3 models LOADED |
| High latency on cold-start | Triton JIT compile | Warm up with 10 sample calls on service start |
| Signals not arriving | NATS subject mismatch or mock-source not running | nats stream info; docker compose logs |
| Feed sync fails | mock-misp not serving | curl http://localhost:7300/feed; check volume mount |
| Training job OOM | Dev corpus too large for container mem | Reduce TRAIN_WINDOW_DAYS |
| Model deploy fails | Artifact uri mismatch between MinIO + model registry | Check fraud.models.artifact_uri; verify S3 object exists |
9. Optional: running training on GPU
If you have NVIDIA GPU locally and nvidia-container-toolkit:
- Uncomment the
deploy.resourcesblock intriton-devservice. - Uncomment GPU reservation in the training container (not shown — in
ml/fraud-intel/Dockerfile.train). docker compose up— Triton will initialise with GPU backend.
For Linux workstations without GPU, the CPU backend is sufficient for dev — accuracy numbers will match production but inference will be ~10× slower, which is fine for correctness testing.