# Detecção de rede de fraude

<https://unifokal.com/docs/modulos/fraud-network>

## Detecção de rede de fraude

O módulo `fraud_network` responde uma pergunta só: **esta pessoa está ligada a outras contas suas?** A ligação é procurada por aparelho, rede, e-mail, telefone, rosto (quando a Detecção de Múltiplas Contas está no mesmo flow) e pela conta que você informa no `reference_id`. Ele **não pede foto nem passo novo** ao titular.

! **A rede é sempre a sua.** Nenhum dado seu alimenta a rede de outro cliente, e você nunca vê dado de cliente nenhum. E **ligação não é prova de fraude**: família no mesmo wi-fi, escritório de contabilidade com dezenas de empresas no mesmo IP e casal com um telefone só continuam `approved`, com a ligação visível para você. Só quando há evidência de **pessoa** (o mesmo rosto ou a mesma conta em documentos diferentes) a verificação vai para `review`. Nunca reprova sozinho.

O veredito sai em `decision`, com **cinco** valores possíveis, e ele depende das **classes** de evidência, nunca do número de chaves. `clean`: componente de um titular só, ou uma classe única (a família no mesmo wi-fi cai aqui). `linked_weak`: duas classes ou mais, **nenhuma** delas de pessoa, e a decisão não muda (é aqui que cai o escritório de contabilidade). `linked`: duas classes ou mais com ao menos uma de **pessoa**, e vai para revisão. `ring`: o componente alcança um titular já rotulado como fraude confirmada **e** há aresta de pessoa no caminho, também para revisão. `insufficient_keys`: havia menos de duas chaves utilizáveis, então não olhamos rede nenhuma, e esse caso **não é cobrado**. As classes são exatamente três, `local` (IP, aparelho), `contato` (e-mail, telefone) e `pessoa` (rosto, conta), e `subject_resolution` diz sobre **quem** o laudo falou: `subject` (o documento lido), `reference` (o `reference_id` que você mandou) ou `verification` (não havia nem um nem outro).

```
// check_details do fraud_network: o TIPO da ligação, nunca o valor da chave
// "linked" sai como passed:null / pending (indeterminado, vai a revisão), NUNCA como recusa
{ "module": "fraud_network", "passed": null, "outcome": "pending", "score": 50,
  "data": { "decision": "linked",
            "component_size": 3, "hops": 1,
            "evidence_classes": ["local", "pessoa"],   // local | contato | pessoa
            "key_kinds": ["account", "ip"],            // os tipos que LIGARAM
            "keys_observed": ["account", "ip"],        // os tipos que foram OLHADOS
            "linked_subjects_by_kind": { "ip": 3, "account": 2 },
            "link_meanings": {
              "account": "A mesma conta do seu sistema (reference_id) apareceu com documentos de titulares diferentes. Vinculo de PESSOA: indica reciclagem de conta.",
              "ip": "Mais de um titular usou o mesmo endereco de rede na janela. Vinculo de LOCAL: casas, escritorios e redes moveis compartilham IP legitimamente." },
            "linked_references": ["acc_401", "acc_402"],
            "linked_confirmed_fraud": false,
            "subject_resolution": "subject",
            "hub_keys_skipped": 0,
            "window_days": 30,
            "thresholds": { "min_subjects": 3, "hub_degree": 50,
                            "max_hops": 3, "window_days": 30 },
            "network_risk": null,     // camada em SOMBRA (ver abaixo): null = NÃO MEDIDO
            "sync_burst": { "shadow": true, "weight": 0, "subjects": 1, "by_kind": {},
                            "suspect": false, "window_minutes": 60, "min_subjects": 3 } } }

// decision "ring": o componente alcança fraude JÁ confirmada, com aresta de pessoa no caminho.
// Os dois sinais em sombra aparecem preenchidos, e MESMO ASSIM não mudam o veredito.
// É o ÚNICO passed:false do módulo, e mesmo ele só pede revisão humana.
{ "module": "fraud_network", "passed": false, "outcome": "failed", "score": 25,
  "data": { "decision": "ring",
            "component_size": 4, "hops": 2,
            "evidence_classes": ["local", "pessoa"],
            "key_kinds": ["device", "face"],
            "keys_observed": ["account", "device", "face", "ip"],
            "linked_subjects_by_kind": { "device": 4, "face": 3 },
            "link_meanings": {
              "device": "Mais de um titular usou o mesmo aparelho na janela. Vinculo de LOCAL: celular emprestado na mesma casa e causa legitima comum.",
              "face": "O mesmo rosto apareceu em documentos de titulares diferentes. Vinculo de PESSOA: nao ha causa legitima cotidiana." },
            "linked_references": ["acc_918", "acc_a22", "acc_c07"],
            "linked_confirmed_fraud": true,
            "subject_resolution": "subject",
            "hub_keys_skipped": 1, "window_days": 30,
            "thresholds": { "min_subjects": 3, "hub_degree": 50,
                            "max_hops": 3, "window_days": 30 },
            "network_risk": 62,       // 1..100, exposição propagada. PESO 0 na decisão.
            "sync_burst": { "shadow": true, "weight": 0, "subjects": 3,
                            "by_kind": { "device": 3 }, "suspect": true,
                            "window_minutes": 60, "min_subjects": 3 } } }
```

`link_meanings` traz, em texto, **o que cada vínculo significa e o que ele não prova**, e só para os tipos que de fato ligaram. Ele existe porque a leitura errada ("ligado, logo fraudador") é o falso positivo número um deste módulo, e o operador que abre o webhook não deve precisar desta página aberta do lado. `linked_subjects_by_kind` responde a outra metade da pergunta, **quantos titulares por qual vínculo**: é ela que deixa você ler por que a família no mesmo wi-fi não foi acusada. Ela sai também no `clean`; já `linked_references` vem vazia em `clean` e `insufficient_keys`, de propósito, porque listar contas sem veredito seria entregar vizinhança de rede sem afirmação nenhuma.

**Dois campos são sinais em sombra, e sombra aqui significa peso zero.** `network_risk` é a exposição do titular a fraude confirmada dentro do **seu próprio** grafo, propagada a partir dos rótulos que você mesmo confirmou (de 1 a 100, decaindo com a distância e com a idade da aresta). `sync_burst` mede **sincronia**: quantos titulares distintos passaram pelas mesmas chaves na última hora (`subjects` inclui o próprio titular, e `by_kind` só lista tipo em que houve ao menos um outro). Os dois viajam para dar contexto à sua revisão e para você medir prevalência antes de confiar neles. Nenhum dos dois entra na decisão: `suspect: true` ou `network_risk: 90` não alteram `decision`, `score`, `passed` nem `outcome`. E `null` nos dois significa **não medido**, jamais "medido e limpo": um zero fabricado seria a afirmação errada.

O que **não sai**, e é o coração do módulo: **o valor de nenhuma chave**. Nem IP, nem e-mail, nem telefone, nem documento, **e nem o hash deles**. Sai o **tipo** (`key_kinds`) e a **classe** da evidência. Devolver o hash pareceria inofensivo e não é: ele é determinístico, e dois payloads bastariam para você **confirmar** que dois titulares compartilham o mesmo e-mail sem nunca ter visto o e-mail.

`keys_observed` viaja ao lado de `key_kinds` porque as duas respondem perguntas opostas: **não achei ligação por rosto** e **não havia rosto para olhar** (flow sem `face_unica`) não podem sair iguais. `linked_references` são os `reference_id` das contas **do seu próprio tenant**: sem eles você leria "há ligação" e não conseguiria agir.
