Skip to main content

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

ToolVersionNotes
Node.js20 LTSnvm
pnpm9.xcorepack enable
Python3.11Training pipeline
Docker Engine24+Compose v2
grpcurllatestgRPC testing
psql16Postgres client
redis-cli7Redis client
nats CLI0.1.5+NATS testing
docker-compose-nvidia-gpuoptionalGPU inference in dev (can skip with CPU fallback)
jqlatest

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

VariableDefault (dev)Purpose
NODE_ENVdevelopment
LOG_LEVELdebugPino level
DATABASE_URLpostgres://...Fraud schema
REDIS_URLredis://...Score cache + feature LRU
NATS_URLnats://nats:4222Event bus
GRPC_PORT50061
HTTP_PORT3061Admin + health
TRITON_URLhttp://triton-dev:8000Model serving
TRITON_MODEL_*Per-category model name
ML_ENABLEDtrueToggle to disable ML and force rule-based
MISP_FEED_URLmockExternal feed
OBJECT_STORE_ENDPOINTminioModel artifact + training data
FEEDBACK_ENABLEDtrueT&S correction API

8. Troubleshooting

SymptomLikely causeFix
Score returns zero fraud consistentlyModel not loadedCheck Triton /v2/models/ — ensure all 3 models LOADED
High latency on cold-startTriton JIT compileWarm up with 10 sample calls on service start
Signals not arrivingNATS subject mismatch or mock-source not runningnats stream info; docker compose logs
Feed sync failsmock-misp not servingcurl http://localhost:7300/feed; check volume mount
Training job OOMDev corpus too large for container memReduce TRAIN_WINDOW_DAYS
Model deploy failsArtifact uri mismatch between MinIO + model registryCheck fraud.models.artifact_uri; verify S3 object exists

9. Optional: running training on GPU

If you have NVIDIA GPU locally and nvidia-container-toolkit:

  1. Uncomment the deploy.resources block in triton-dev service.
  2. Uncomment GPU reservation in the training container (not shown — in ml/fraud-intel/Dockerfile.train).
  3. 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.