> For the complete documentation index, see [llms.txt](https://docs.ipcheck.ing/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ipcheck.ing/developer/pt-br/architecture/ip-data-sources.md).

# Fontes de Dados de IP

Como várias fontes de geolocalização de IP são consultadas e normalizadas em uma única forma canônica.

Duas perguntas impulsionam a maior parte do MyIP, e elas são respondidas por mecanismos diferentes:

* **"Qual é o meu IP?"** — o navegador consulta vários endpoints de eco de terceiros diretamente. O back-end não participa.
* **"Onde este IP está?"** — o navegador consulta *nosso* back-end, que consulta um provedor de geolocalização (ou um banco de dados local) e normaliza a resposta.

Mantê-los separados é intencional: um endpoint de eco precisa ver a própria conexão do visitante, então ele não pode ser passado por proxy.

```mermaid
flowchart TD
    subgraph Navegador
      G["utils/getips/ — executa runChain() sobre um provedor por fonte"]
      T["utils/transform-ip-data.js"]
      C["cartões de IP"]
    end
    subgraph Back-end
      H["Manipuladores de geolocalização em api/<br/>ipinfo-io, ipapi-com, ipapi-is,<br/>ip2location-io, ip-sb, ipcheck-ing, maxmind"]
      M["common/maxmind-service.js — mmdb local"]
    end
    P["Endpoints de eco de terceiros<br/>Cloudflare, IPCheck.ing, IPIP.net, ..."]
    U["Provedores de geolocalização"]

    G -->|"busca direta"| P
    G -->|"o IP"| H
    H --> U
    H --> M
    H -->|"JSON canônico"| T --> C
```

## Etapa 1 — resolvendo seu próprio IP

`frontend/utils/getips/` contém um pequeno módulo por fonte. Cada um exporta um **objeto de provedor**:

```js
{ id: 'cloudflare-v4', name: 'Cloudflare IPv4', run: async (originalSite) => '203.0.113.7' }
```

A única tarefa de um provedor é consultar um upstream — por meio de `fetchWithTimeout` (padrão de 5 segundos no navegador) — e resolver a string bruta de IP, ou lançar um erro. Ele não valida e não conhece nenhum outro provedor.

A validação e o fallback ficam um nível acima, no `runChain()` executor exportado por `frontend/utils/getips/index.js`. Ele percorre seus provedores em ordem e retorna o primeiro salto cuja resposta passa em `isValidIP()` de `common/valid-ip.js`, como `{ ip, source }` onde `source` é o nome de exibição do provedor vencedor. Uma cadeia esgotada resolve `{ ip: null, source }` carregando o nome do último provedor — o executor nunca lança erro.

`frontend/components/IpInfos.vue` renderiza até seis cartões e atribui uma cadeia a cada um, por índice:

| Cartão | Fonte primária     | Endpoint                               | Faz fallback para                   |
| ------ | ------------------ | -------------------------------------- | ----------------------------------- |
| 0      | IPCheck.ing IPv4   | `4.ipcheck.ing`                        | IPify IPv4 (`api4.ipify.org`)       |
| 1      | IPCheck.ing IPv6   | `6.ipcheck.ing`                        | IPify IPv6 (`api6.ipify.org`)       |
| 2      | Cloudflare IPv4    | `1.0.0.1/cdn-cgi/trace`                | MyExternalIP IPv4                   |
| 3      | Cloudflare IPv6    | `[2606:4700:4700::1111]/cdn-cgi/trace` | MyExternalIP IPv6                   |
| 4      | IPIP.net           | `myip.ipip.net/json`                   | Upai (`pubstatic.b0.upaiyun.com`)   |
| 5      | IPCheck.ing IPv6/4 | `64.ipcheck.ing`                       | — (JSON, depois o `/cdn-cgi/trace`) |

{% hint style="info" %}
As três fontes do IPCheck.ing recebem um argumento `originalSite` . Na implantação canônica, elas leem o endpoint JSON; em outros lugares, leem o texto no estilo Cloudflare do mesmo host. Se a chamada JSON falhar, elas tentam novamente via trace antes de desistir. `/cdn-cgi/trace` O tratamento de falhas é em camadas:
{% endhint %}

Dentro de uma cadeia

1. **, um provedor que lança erro e um provedor que responde com lixo são tratados da mesma forma:**&#x72;egistra um `runChain()` console.warn `e avança para o próximo provedor. Uma resposta malformada não encerra a cadeia — ela só custa aquele salto. Se nenhum provedor produzir um IP válido, a cadeia resolve` Cada cartão executa seu próprio pipeline de resolver e depois detalhar, e os seis rodam sob `{ ip: null, source }`.
2. Promise.allSettled `, então uma fonte inativa não derruba o lote. Os cartões são renderizados independentemente conforme chegam.`; então uma fonte inativa não derruba o lote. Os cartões são renderizados independentemente conforme chegam.
3. Quando a cadeia inteira de um cartão falha, a SPA emite um evento `ip-source:exhausted` no barramento do app. O Sentry (quando configurado) só o captura se outro cartão da mesma versão de IP tiver resolvido — caso contrário, "nossa cadeia falhou" é indistinguível de um visitante sem IPv6, que é ruído rotineiro.

As falhas individuais de fontes são `e avança para o próximo provedor. Uma resposta malformada não encerra a cadeia — ela só custa aquele salto. Se nenhum provedor produzir um IP válido, a cadeia resolve` intencionais, então nunca chegam ao monitoramento de erros; o evento de exaustão por cartão é o sinal de saúde.

## Etapa 2 — geolocalizando um IP

Assim que um cartão tem um IP, ele chama o back-end. `frontend/data/ip-databases.js` é o registro das fontes selecionáveis:

| id | Nome           | Endpoint                                  | Precisa de chave                                         |
| -- | -------------- | ----------------------------------------- | -------------------------------------------------------- |
| 0  | IPCheck.ing    | `/api/ipchecking?ip={{ip}}&lang={{lang}}` | `IPCHECKING_API_KEY` (API privada)                       |
| 1  | IPinfo.io      | `/api/ipinfo?ip={{ip}}`                   | Opcional (`IPINFO_API_KEY`)                              |
| 2  | IP-API.com     | `/api/ipapicom?ip={{ip}}&lang={{lang}}`   | Não                                                      |
| 3  | IPAPI.is       | `/api/ipapiis?ip={{ip}}`                  | `IPAPIIS_API_KEY`                                        |
| 4  | IP2Location.io | `/api/ip2location?ip={{ip}}`              | `IP2LOCATION_API_KEY`                                    |
| 5  | IP.sb          | `/api/ipsb?ip={{ip}}`                     | Não                                                      |
| 6  | MaxMind        | `/api/maxmind?ip={{ip}}&lang={{lang}}`    | Banco de dados local, sem chave no momento da requisição |

`buildDbUrl(db, ip, lang)` substitui os `{{ip}}` e `{{lang}}` placeholders. `{{lang}}` é o `apiTag` do registro de locale para o locale ativo da interface, e não o próprio código da UI — a tag pela qual os bancos de dados upstream localizam os nomes dos lugares. Nenhum handler a valida; veja [i18n](/developer/pt-br/development/i18n.md). O estado `habilitado` de cada fonte é derivado das `/api/configs` feature flags quando são carregadas (`applyConfigAvailability`) — fontes protegidas por chave seguem sua flag, fontes sem chave ficam sempre disponíveis — e falhas de busca em tempo de execução nunca a alteram. Os usuários trocam de fonte nas Preferências; se uma escolha armazenada aponta para uma fonte que não está mais configurada, ela é migrada para a mais próxima disponível (`nearestEnabledId`, percorrendo para frente na ordem dos ids) com um toast — o único caso em que a preferência armazenada é regravada. A configuração de chaves é coberta em [Chaves de API opcionais](/developer/pt-br/configuration/optional-api-keys.md).

### Cadeia de fallback no lado do cliente

`IpInfos.vue` não consulta simplesmente a fonte preferida e desiste. Dentro de `fetchIPDetails()`:

* A fonte solicitada é procurada na **habilitado** lista; se ela estiver ausente (a flag de config está desligada, ou as configs ainda não carregaram), a busca começa na primeira habilitada.
* Em caso de erro, registra, avança para a próxima fonte habilitada e tenta novamente — até que todas as fontes habilitadas tenham sido tentadas.
* Cair em uma fonte diferente da solicitada mostra um toast único e desloca a fonte de tempo de execução da sessão, para que consultas posteriores comecem pela que funciona — a preferência armazenada nunca é regravada.
* Os resultados são cacheados por IP, e requisições em andamento são deduplicadas por IP, então seis cartões mostrando o mesmo endereço produzem uma única requisição.

## A forma canônica da resposta

Todo manipulador de geolocalização retorna o mesmo JSON, independentemente de como era o upstream. De `api/ipinfo-io.js`:

```js
{
    ip,
    city,
    region,
    country,        // código ISO 3166-1 alpha-2
    country_name,
    country_code,   // mesmo código de `country`
    latitude,
    longitude,
    timezone,       // "Asia/Singapore" — adicionado pelo middleware, não pelo handler
    asn,            // "AS13335" — com o prefixo AS
    org
}
```

| Campo                      | Observações                                                                                               |
| -------------------------- | --------------------------------------------------------------------------------------------------------- |
| `ip`                       | Ecoado pelo upstream                                                                                      |
| `city` / `region`          | Strings livres; `'N/A'` quando o upstream não tem nada                                                    |
| `country` / `country_code` | Ambos carregam o código ISO alpha-2                                                                       |
| `country_name`             | Nome próprio do upstream — a interface geralmente o substitui                                             |
| `latitude` / `longitude`   | Números                                                                                                   |
| `timezone`                 | nome da zona IANA, `''` quando as coordenadas são inutilizáveis. Veja abaixo                              |
| `asn`                      | Normalizado para a forma `AS<number>` ; fontes que retornam apenas um número recebem o prefixo adicionado |
| `org`                      | Nome da organização ou do provedor de internet                                                            |

Duas fontes o estendem. `api/ipapi-is.js` adiciona `isHosting` e `isProxy` booleans. `api/ipcheck-ing.js` é um proxy pass-through para a API privada do IPCheck.ing e retorna o payload dessa API literalmente, incluindo um `advancedData` objeto que o frontend desmembra em campos de proxy, tipo de IP, IP nativo, score de qualidade, protocolo e provedor.

Os `advancedData` campos nem sempre são valores reais: para chamadas sem login, ou para chamadas logadas que já passaram da cota mensal, o upstream substitui cada campo no bloco pela string sentinela `sign_in_required` ou `quota_exceeded`. `gatedSentinel()` em `frontend/utils/transform-ip-data.js` propaga ambos literalmente para que a UI possa escolher o prompt correspondente. Nenhum dos estados é um modo de falha — a resposta ainda é uma `200` com dados geográficos completos, nada gera erro e nenhuma fonte de fallback é tentada.

### `timezone` vem do middleware, não da fonte

Nenhum handler produz `timezone`, e nenhum campo upstream é lido para isso. O `withTimeZone()` middleware (`common/ip-timezone.js`), anexado a todas as sete rotas de geo, resolve a `latitude` / `longitude` já presente na resposta para um nome de zona IANA e o adiciona na saída — apenas 2xx, então corpos de erro nunca recebem o campo. Uma nova fonte de geo o herda adicionando o middleware à sua rota; o proxy pass-through não precisa de mudança no upstream.

Derivá-lo das próprias coordenadas da resposta é justamente o objetivo: perguntar a um segundo banco de dados sobre o mesmo IP acabaria colocando-o em outro lugar e imprimindo uma zona que contradiz a cidade ao lado. Coordenadas que a fonte não conseguiu resolver (`'N/A'`, ausentes ou um ponto em água aberta) geram `''` em vez de um palpite — esses payloads também não trazem cidade, então o cartão omite ambos.

Apenas o nome da zona é enviado. As rotas de geo ficam atrás de um cache de borda de 24 horas, e um offset UTC em cache estaria errado em uma hora para todo visitante entre uma mudança de DST e a expiração da entrada; a interface renderiza o offset por visualização em vez disso.

<details>

<summary>O que o frontend faz com o payload</summary>

`frontend/utils/transform-ip-data.js` transforma uma resposta canônica em dados do cartão:

* `country_name` é rederivado do código **do país** localmente para que cada fonte mostre o mesmo nome no idioma da interface; a string do upstream é apenas um fallback.
* `country_code` de `'N/A'` vira uma string vazia. O código também serve como chave de consulta para o mapa de calor de atividade do país (`/api/cfradar?view=country-traffic&country=`), e um código vazio simplesmente oculta esse ponto de entrada.
* `org` torna-se `isp`, e um `asn` que começa com `AS` recebe um `asnlink` para bgp.tools.
* `timezone` passa inalterado; o cartão o combina com um offset UTC calculado no navegador (`frontend/utils/time-utils.js`).
* ). As coordenadas são arredondadas para uma casa decimal para `mapUrl` / `mapUrl_dark`, que apontam para `/api/map`. Na escala de zoom do mapa, \~0,176° é um pixel, então 0,1° fica abaixo de um pixel — o marcador parece idêntico enquanto todo IP na mesma célula da grade colapsa para uma única chave de edge cache. As coordenadas com precisão total são mantidas para exibição.
* Para a fonte `0` (IPCheck.ing) ele também extrai `advancedData` os campos.

</details>

Adicionar uma fonte significa escrever um handler que produza esta forma e adicionar uma linha a `data/ip-databases.js`. Novos handlers devem usar a `makeGeoHandler({ name, buildUrl, normalize })` fábrica em `common/geo-handler.js`, que cuida do invólucro compartilhado: lê o `?ip`, já validado, busca por meio de `fetchUpstream`, lança erro em um status não-2xx (páginas de indisponibilidade vêm como HTML e, sem isso, quebrariam `JSON.parse`), normaliza, responde, e registra e retorna 500 em caso de falha.

## Conjuntos de dados locais

Dois conjuntos de dados vivem em disco dentro de `common/` e são lidos de forma síncrona no momento da requisição. Ambos têm um autoatualizador que roda no mesmo processo.

### MaxMind GeoLite2

`common/maxmind-service.js` abre `GeoLite2-City.mmdb` e `GeoLite2-ASN.mmdb` de `common/maxmind-db/` e mantém ambos os leitores em memória. `lookupMaxMind(ip, lang)` mescla um registro de City e um de ASN na forma canônica, resolvendo nomes localizados com um fallback em inglês e depois `'N/A'` em português. Se algum leitor estiver faltando, lança com `statusCode` 503, e `/api/maxmind` responde 503 — a API se degrada, o servidor continua rodando.

A atualização é tratada por `common/maxmind-updater.js`:

| Comportamento                 | Valor                                                                                                                 |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Bootstrap na inicialização    | Baixa apenas se os arquivos estiverem ausentes, limitado a 5 minutos; roda independentemente de `MAXMIND_AUTO_UPDATE` |
| Primeira verificação agendada | 60 segundos após o início                                                                                             |
| Intervalo de repetição        | A cada 24 horas                                                                                                       |
| Condição para o agendador     | `MAXMIND_AUTO_UPDATE=true` mais `MAXMIND_ACCOUNT_ID` e `MAXMIND_LICENSE_KEY`                                          |
| Concorrência                  | Um arquivo de bloqueio, considerado obsoleto após 2 horas                                                             |

Os downloads são preparados e publicados atomicamente. Um watcher de arquivo separado (`startMaxMindFileWatcher()`) consulta ambos os arquivos a cada 5 segundos e recarrega os leitores quando outro processo os substitui, com debounce de 1 segundo para que City e ASN sejam aplicados como uma única recarga. Se o novo arquivo for inválido, os leitores existentes permanecem no lugar. As instruções de configuração estão em [Configuração do MaxMind](/developer/pt-br/getting-started/maxmind-setup.md).

### CAIDA as2org e relacionamentos AS

`common/caida-updater.js` gerencia dois conjuntos de dados com a mesma mecânica de lock / estado / atomic-publish / validate / reload:

| Conjunto de dados | Arquivo                            | Fonte                                                                                          | Usado por                                                                                                         |
| ----------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `as2org`          | `common/as-org-db/as-org2info.txt` | `publicdata.caida.org/datasets/as-organizations/latest.as-org2info.txt.gz`                     | Consultas de nome de organização para AS → organização                                                            |
| `as-rel`          | `common/as-rel-db/as-rel2.txt`     | Mais recente `*.as-rel2.txt.bz2` em `publicdata.caida.org/datasets/as-relationships/serial-2/` | O grafo de conectividade dos AS e a opção de fallback da contagem de relacionamentos para `/api/cfradar?view=asn` |

`as2org` tem um `latest` symlink upstream, então um `HEAD` mais `Last-Modified` é suficiente para detectar um novo snapshot. `as-rel` não tem nenhum, então o atualizador raspa a listagem do diretório e pega o lexicograficamente mais recente `YYYYMMDD` nome do arquivo. Ambos são descompactados em tempo real e validados antes de serem publicados.

A programação espelha a do MaxMind: faz bootstrap na inicialização se estiver ausente (limite de 2 minutos), primeira verificação 60 segundos após o início, depois a cada 24 horas, com o agendador periódico condicionado a `CAIDA_AUTO_UPDATE=true`. A inicialização sempre é executada, então um checkout novo funciona.

`common/as-rel-db.js` analisa as linhas delimitadas por pipe e mantém os relacionamentos de provedor para cliente (`-1`) e de peer para peer (`0`) — linhas irmãs são ignoradas — construindo um índice cliente → provedores, um índice simétrico de peers e uma contagem de clientes por AS. O conjunto Tier 1 é derivado do snapshot, em vez de codificado fixamente: um AS sem provedores na topologia p2c que também fornece trânsito para pelo menos 100 outros. `/api/asn-connectivity` então faz uma BFS totalmente local e síncrona a partir do AS de origem até os Tier 1s, emitindo `trânsito` arestas do índice p2c e `peering` arestas quando uma rede alcança a clique Tier 1 sem liquidação — que é também do que consiste o próprio grafo de origem de um Tier 1.

`common/as-org-db.js` analisa o TXT delimitado por pipe da CAIDA (\~12 MB) em vez do equivalente JSONL (\~28 MB) — conteúdo idêntico, e `split('|')` supera `JSON.parse` por linha em cerca de 40%. Ambos os módulos escolhem o arquivo correspondente mais recente pela hora de modificação, então um snapshot baixado manualmente com um nome diferente ainda funciona.

## O que acontece quando algo falha

| Falha                                                       | Resultado                                                                                                                                                   |
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Um endpoint de echo está fora do ar                         | `runChain()` avança para o próximo provedor na cadeia desse cartão; se todos os provedores falharem, o cartão não mostra nada e emite `ip-source:exhausted` |
| Um provedor de geolocalização apresenta erro                | O cliente faz fallback para a próxima fonte habilitada e exibe um toast; consultas posteriores começam pela fonte que está funcionando                      |
| Toda fonte de geolocalização falha para um IP               | Os campos de detalhes do cartão permanecem vazios; o erro é registrado no lado do cliente                                                                   |
| Um upstream trava                                           | `fetchUpstream` interrompe após 8 segundos e o handler retorna `500 { error }`                                                                              |
| Bancos de dados do MaxMind ausentes ou inválidos            | `/api/maxmind` retorna 503; o restante da API não é afetado                                                                                                 |
| Snapshots da CAIDA ausentes                                 | O grafo de conectividade retorna vazio, e as consultas de nome de organização fazem fallback para o as-overview do RIPEstat `as-overview`                   |
| Um upstream retorna uma página de indisponibilidade em HTML | `makeGeoHandler` lança erro no status diferente de 2xx antes de fazer o parsing                                                                             |

## Páginas relacionadas

* [Backend](/developer/pt-br/architecture/backend.md) — proteções, camadas de cache e a sequência de inicialização
* [Chaves de API opcionais](/developer/pt-br/configuration/optional-api-keys.md) — quais fontes precisam de credenciais
* [Configuração do MaxMind](/developer/pt-br/getting-started/maxmind-setup.md) — obtendo e atualizando o GeoLite2
* [Endpoints da API](/developer/pt-br/reference/api-endpoints.md) — o contrato completo de requisição/resposta


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ipcheck.ing/developer/pt-br/architecture/ip-data-sources.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
