> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Visão geral do Fetcher Engine

> O Fetcher Engine é um módulo Go importável que executa a extração de dados no próprio processo: o modelo de três camadas, a fronteira de importação imposta e os modos Direct e Store.

O **Fetcher Engine** é o núcleo de extração do Fetcher, empacotado como um módulo Go que você pode importar. Aplicações hospedeiras como o Matcher e o Reporter executam o Engine no próprio processo, em vez de operar uma implantação separada do Fetcher. O [Manager e o Worker](/pt/fetcher/what-is-fetcher) autônomos são, eles mesmos, hospedeiros sobre o mesmo Engine.

O Engine é dono das regras de extração: ciclo de vida da conexão, descoberta e validação de esquema, planejamento de consultas, execução da extração, contratos de resultado e de erro, limites e segurança por tenant. Ele não é dono de nenhuma infraestrutura.

<Note>
  O Engine é um **módulo Go distinto** dos serviços do Fetcher. O caminho do módulo é `github.com/LerianStudio/fetcher/pkg/engine`, e o módulo dos serviços é `github.com/LerianStudio/fetcher/v2`. O Engine tem sua própria linha de versões, com tags prefixadas pelo caminho (`pkg/engine/vX.Y.Z`). Uma importação do Engine não herda nenhuma dependência dos serviços.

  O Fetcher é source-available sob a Elastic License 2.0, e o Engine carrega a mesma licença. Você lê as regras de extração que incorpora.
</Note>

## O modelo de três camadas

***

| Camada                             | Pacote                                   | É dona de                                                                                                                                               |
| ---------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Núcleo do Engine**               | `pkg/engine`                             | As *regras* de extração. Depende apenas das interfaces de porta fornecidas pelo hospedeiro, nunca de infraestrutura.                                    |
| **Adaptadores de compatibilidade** | `pkg/enginecompat/*`                     | Pontes entre as portas do Engine e a infraestrutura real do Fetcher — MongoDB, Redis e os drivers de fonte de dados.                                    |
| **Aplicação hospedeira**           | Manager, Worker ou o seu próprio serviço | A camada operacional: autenticação, aplicação de licença, rotas HTTP, filas, armazenamentos de estado, storage, telemetria e ciclo de vida do processo. |

O princípio que guia o desenho: **o Engine é dono do que faz o Fetcher ser *Fetcher*, e as aplicações hospedeiras são donas de *como* o Fetcher roda.**

Todo produto que incorpora o Engine compartilha, portanto, um único dono canônico do comportamento de fonte de dados e de extração. Uma mudança de regra no núcleo chega a todos os hospedeiros na próxima atualização do módulo, e nenhum hospedeiro a reimplementa.

## A fronteira de importação

***

O módulo do Engine declara **zero dependências de terceiros**. O `go.mod` dele não tem bloco `require` algum, e um teste imposto no build mantém as coisas assim.

Duas proteções rodam dentro do módulo a cada `go test ./...`, e o workflow de CI do módulo as executa com o workspace do Go desligado:

* **A lista de permissão.** Toda dependência transitiva de `pkg/engine` precisa ser um pacote da biblioteca padrão do Go ou um pacote local ao módulo do Engine. Qualquer outra família de importação quebra o build, mesmo uma família que nenhuma lista de bloqueio cita.
* **Uma lista de bloqueio nomeada.** Classes enumeradas falham em cima da lista de permissão. Elas cobrem frameworks HTTP, message brokers, drivers de banco de dados, SDKs de object storage, middleware de runtime de tenant, as bibliotecas de autenticação e de licença, e invólucros da biblioteca padrão como `database/sql`, `net/http` e `os/exec`.

Um passo separado da CI procura por uma linha `require` no `go.mod` do Engine e falha o job quando encontra uma.

### Por que isso importa quando você incorpora

* **Sem conflitos de dependência.** O Engine não consegue puxar um driver, um cliente de broker ou um framework HTTP para o grafo do seu módulo. O seu hospedeiro mantém controle total das próprias versões.
* **Sem I/O oculto.** O núcleo não consegue abrir um socket, um arquivo ou um banco de dados por conta própria. Cada byte que entra e sai atravessa uma porta que você forneceu.
* **Um ponto de substituição estável.** Rode o Engine inteiro contra o harness em memória nos testes. Troque pelos adaptadores reais em produção, sem mudar o código que chama.

## Modo Direct e modo Store

***

Um plano de extração carrega um modo com três valores: `direct`, `store` e o valor zero `auto`.

O Engine resolve `auto` a partir das portas que você ligou. Com um `ResultSink` configurado, ele escolhe o modo store; sem um, escolhe o modo direct. Ou seja, o modo é consequência da sua composição, não uma chave separada. Um pedido explícito de `store` sem sink falha logo no início, como erro de validação, antes de o Engine tocar em qualquer fonte de dados.

Os dois modos retornam formatos diferentes. Exatamente um braço do resultado é não nulo, e o JSON omite o braço não usado por completo.

### Modo Direct

O Engine executa os passos do plano e junta as linhas em um único mapa. As chaves do mapa são o nome da configuração da fonte de dados e, em seguida, a tabela qualificada. O Engine serializa esse mapa uma vez como JSON indentado e retorna os bytes inline.

```json theme={null}
{
  "pg-main": {
    "public.users": [
      {
        "email": "a@example.com",
        "id": 1
      }
    ]
  }
}
```

O resultado inline carrega estes metadados:

| Campo           | Valor no modo direct                                     |
| --------------- | -------------------------------------------------------- |
| `data`          | O payload serializado acima                              |
| `format`        | `json`                                                   |
| `rowCount`      | Total de linhas em todas as tabelas                      |
| `plaintextSize` | Tamanho em bytes do payload                              |
| `integrity`     | Algoritmo `SHA-256` mais o digest hexadecimal do payload |
| `protection`    | `encrypted: false`, aplicado por `engine`                |

A saída do modo direct é determinística. O serializador ordena as chaves do mapa. A mesma entrada, portanto, dá a você um JSON idêntico byte a byte e o mesmo digest, qualquer que tenha sido a ordem em que os passos paralelos terminaram.

O seu hospedeiro é dono de tudo depois disso. O Fetcher Worker, por exemplo, assina o texto claro com HMAC-SHA256 e o criptografa antes de armazenar os bytes.

### Modo Store

O Engine abre um stream no seu `ResultSink` e escreve o resultado de forma incremental, com memória constante. Ele nunca segura o resultado inteiro. Um único escritor drena as goroutines de extração estritamente na ordem crescente dos passos do plano, e o digest de integridade cobre exatamente os bytes escritos.

O formato de saída é NDJSON — um objeto JSON por linha, terminado por quebra de linha, sem array envolvente:

```json theme={null}
{"config":"pg-main","table":"public.users","row":{"id":1,"email":"a@example.com"}}
```

A chamada retorna uma referência em vez de bytes:

| Campo                      | Significado                                                                                                                                                                                                                               |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `path`                     | A localização **lógica** no seu storage. A referência não expõe o tipo físico de backend — o seu adaptador resolve o caminho.                                                                                                             |
| `format`                   | Formato de saída dos bytes persistidos                                                                                                                                                                                                    |
| `rowCount`                 | Total de linhas no resultado persistido                                                                                                                                                                                                   |
| `sizeBytes`                | Tamanho serializado escrito                                                                                                                                                                                                               |
| `integrity` e `protection` | O que o seu sink reportou. Quando o sink não reporta integridade, o Engine carimba o digest SHA-256 que calculou sobre os bytes transmitidos. O Engine valida apenas que `protection.appliedBy` é um entre `engine`, `adapter` ou `host`. |

Em um aborto — um erro de escrita, um limite de tamanho excedido ou um contexto cancelado — o Engine abandona o escritor e nunca chama `Close`. Um resultado parcial, portanto, nunca vira uma referência retornada. Trate um escritor não fechado como uma escrita descartada.

## Contratos de falha e de resultado

***

* **Falha rápida entre fontes de dados.** O primeiro passo que falha interrompe a execução. O Engine nunca retorna um resultado parcial.
* **Erros redigidos.** Um erro de driver pode embutir uma DSN, uma credencial ou detalhes internos do driver, então o Engine o descarta e retorna uma mensagem fixa no lugar.
* **Onze categorias de erro.** `validation`, `not_found`, `unauthorized`, `forbidden`, `limit_exceeded`, `conflict`, `unavailable`, `connect`, `timeout`, `canceled` e `internal`. O seu hospedeiro as mapeia para os próprios códigos de transporte. `connect` permanece distinto de `unavailable`, e `timeout` permanece distinto de `canceled`.
* **Cinco status de execução.** `pending`, `running`, `completed`, `failed` e `canceled`. Os três últimos são terminais. Um cancelamento pelo hospedeiro registra `canceled`, e um prazo estourado registra `failed`.
* **Conectores fechados.** Todo conector que o Engine abre é fechado de volta, no caminho de sucesso e em todos os caminhos de falha.

## Limites e escopo de tenant

***

O Engine nunca roda sem limite. Quando você não fornece limites, ele aplica estes padrões:

| Limite                               | Padrão    |
| ------------------------------------ | --------- |
| Fontes de dados por extração         | 10        |
| Tabelas por fonte de dados           | 20        |
| Campos por tabela                    | 50        |
| Workers paralelos de fontes de dados | 4         |
| Tempo limite da extração             | 5 minutos |
| Tamanho do resultado serializado     | 256 MiB   |

Uma requisição pode **reduzir** qualquer limite e nunca aumentá-lo. Um override acima do padrão falha com um erro de validação que nomeia o campo violado. Um override zero ou negativo mantém o padrão.

O Engine impõe o teto de tamanho do resultado duas vezes: um limite inferior barato por passo, que aborta cedo, e uma checagem definitiva sobre o payload indentado final. Um resultado acima do limite nunca chega a você inline e nunca chega ao seu sink.

O escopo de tenant é igualmente estreito. Toda operação carrega um ID de tenant e nada além disso — o Engine não tem conceito de organização nem de produto. Ele valida o ID de tenant antes de qualquer acesso a recurso, e rejeita ali um valor vazio ou malformado.

## Próximos passos

***

<CardGroup cols={2}>
  <Card title="Incorporar o Engine" icon="code" href="/pt/fetcher/fetcher-embedding-the-engine">
    Importe, forneça as portas e construa com um exemplo executável.
  </Card>

  <Card title="Referência de portas" icon="plug" href="/pt/fetcher/fetcher-engine-ports">
    Cada porta, se ela é obrigatória e o que acontece sem ela.
  </Card>
</CardGroup>
