Service Risk Engine
Moteur d'évaluation des risques (fraude, AML, conformité) sur chaque demande de paiement ou transfert. Retourne une décision typée approved / challenged / blocked avec score, raisons audit et trace complète.
Voir aussi /services/risk-engine/preflight pour le mode prefetch utilisé par l'orchestrator en pré-confirmation d'intent (ADR-0007).
Identité
| Attribut | Valeur |
|---|---|
| Package | @nex/service-risk-engine v0.0.1 |
| Port | 3000 |
| Schéma DB | risk_engine (PostgreSQL) |
| Swagger | services/risk-engine/src/main.ts:54 — actif, tags risk-rules, risk-evaluation, fraud-detection |
| README | ❌ absent |
| Owner | @security-team |
Surface API
| Controller | Route | Endpoints |
|---|---|---|
risk-engine.controller.ts | /risk | 3 (POST /evaluate, GET /evaluations/:id, GET /evaluations/:id/archive) |
app.controller.ts | / | 1 (health) |
Pipeline d'évaluation
services/risk-engine/src/application/evaluate-risk.use-case.ts
- Idempotency check (style Stripe, TTL 24 h) — cache hit → retour immédiat de la décision précédente avec son
policyId. - Build context — en parallèle :
- sender KYC level + rejection status (
KycStatusHttpAdapter) - receiver KYC level + rejection status
ResolvedTransactionPolicy(TransactionPolicyHttpAdapter)- velocity aggregates (
LedgerAggregatesHttpAdapter)
- sender KYC level + rejection status (
- Run rule engine — chain of responsibility, court-circuit critique.
- Compose score + decision —
RiskScoringService. - Build
RiskDecision— enrichissement des raisons depuisRISK_REASON_CATALOG. - Persist — audit append-only en DB + register idempotency key.
Fail-closed strict V1
Toute DependencyUnavailableError (KYC, configuration, ledger) → RiskDecision blocked avec RuleCode = DEPENDENCY_UNAVAILABLE. Pas de fail-open. Pas de retry au runtime (timeout 80 ms par adapter, configurable).
Règles V1 (8 rules, 2 couches actives)
| Layer | Rule | Comportement |
|---|---|---|
| 0 | EligibilityFromPolicyRule | Vérifie l'éligibilité depuis la policy résolue |
| 0 | KycRejectedRule | Court-circuite avant KycLevel — si rejeté → blocked |
| 0 | KycLevelRule | Vérifie le niveau KYC requis par la policy |
| 0 | SamePartyForbiddenRule | sender.accountId === receiver.accountId → blocked |
| 1 | PerTransactionLimitRule | Limite par transaction (depuis policy) |
| 2 | CumulativeCapRule (daily) | Plafond journalier cumulé |
| 2 | CumulativeCapRule (weekly) | Plafond hebdomadaire |
| 2 | CumulativeCapRule (monthly) | Plafond mensuel |
Trust boundary prefetched
Le champ RiskEvaluationRequest.prefetched accepte un snapshot de contexte (sender KYC, receiver KYC, sender balance) uniquement depuis un caller avec le scope JWT risk:evaluate:trusted.
Garanties (cf. ADR-0007) :
prefetchedn'accepte jamaispolicy,eligibilityoulimits— 3 barrières : type DTO, ValidationPipe, sanitize runtime.- La
ResolvedTransactionPolicyest toujours re-résolue côté risk-engine viaTransactionPolicyHttpAdapter.
Modèle de données
src/infrastructure/persistence/entities/ :
| Entity | Rôle |
|---|---|
RiskEvaluation | Trace append-only des évaluations (input snapshot, output decision, policyId, durée, rules déclenchées) |
RiskIdempotencyKey | Clés d'idempotency avec TTL 24 h, purgées par cron horaire |
Migrations : src/migrations/. Purge automatique des idempotency keys via @nestjs/schedule (cron horaire).
Dépendances
| Type | Détails |
|---|---|
| Intra-monorepo | @nex/shared-types (brand types, RiskEvaluationRequest/RiskDecision) · @nex/shared-utils (PhaseTimer, RISK_REASON_CATALOG) |
| Inter-services Nex | HTTP synchrone vers configuration (policy resolution), ledger-wallets (velocity aggregates), customer-profiles-kyc (KYC status) |
| Externes notables | @nestjs/schedule, typeorm, pg, jsonwebtoken |
Adapters HTTP (fail-closed)
| Adapter | Cible | Timeout |
|---|---|---|
TransactionPolicyHttpAdapter | POST /v1/configuration/transaction-policy/resolve | 80 ms |
LedgerAggregatesHttpAdapter | velocity aggregates (ledger-wallets) | 80 ms |
KycStatusHttpAdapter | KYC level + rejection status (customer-profiles-kyc) | 80 ms |
Note importante : TransactionPolicyHttpAdapter désérialise les valeurs bigint depuis des string JSON pour préserver la précision monétaire.
Pages à produire
data-model.md— schéma completrisk_evaluations+risk_idempotency_keysrules.md— détail de chaque rule (input, output, code retourné)runbook.md— alertes (taux de blocage anormal, latence adapters)threat-model.md— STRIDE focalisé sur la manipulation deprefetched