> ## 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.

# Puertos del Fetcher Engine

> Referencia de los ocho puertos de capacidad del Fetcher Engine: qué exige cada contrato, si es obligatorio y qué hace exactamente el Engine sin él.

El [Fetcher Engine](/es/fetcher/fetcher-engine-overview) no es dueño de ninguna infraestructura. Llega al mundo exterior solo a través de **puertos** — interfaces Go que tu aplicación anfitriona implementa y pasa a `engine.New`.

Esta página es la referencia de los ocho. La columna **Sin él** es el punto central de la página. La degradación controlada es el contrato con el que planificas, así que léela antes de omitir un puerto.

## Los puertos de un vistazo

***

| Puerto                   | Obligatorio              | Sin él                                                                                                                                            |
| ------------------------ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ConnectorRegistry`      | **Siempre**              | `engine.New` falla. No existe ningún Engine.                                                                                                      |
| `CredentialProtector`    | Con persistencia cifrada | `engine.New` falla cuando la persistencia cifrada está activa. Si no, el Engine no pasa ninguna contraseña suministrada al almacén de conexiones. |
| `ConnectionStore`        | Opcional                 | Las operaciones respaldadas por conexiones fallan; `Limits()`, `AuthorizeConnectionAccess()` y `CheckActiveExecutions()` siguen disponibles.      |
| `ExecutionStore`         | Opcional                 | Sin seguimiento durable del estado de ejecución.                                                                                                  |
| `ResultSink`             | Opcional                 | El modo Store no está disponible. La extracción corre en modo Direct.                                                                             |
| `SchemaCache`            | Opcional                 | El descubrimiento de esquemas siempre va al datasource en vivo.                                                                                   |
| `ActiveExecutionChecker` | Opcional                 | Sin control de conflictos. Las actualizaciones y los borrados de conexiones siempre proceden.                                                     |
| `Observability`          | Opcional                 | Los hooks de trazas quedan en no-op.                                                                                                              |

<Note>
  `engine.New` rechaza un puerto pasado como **nil tipado**, no solo como nil literal. Un valor de interfaz que envuelve un puntero nil falla en la construcción con un error de validación claro, en lugar de entrar en pánico en el primer uso.
</Note>

## ConnectorRegistry

***

**Obligatorio: siempre.** Es el único puerto que el Engine valida sin condiciones.

El registro resuelve una factory de conector por tipo de datasource. No hace E/S, resuelve de forma determinista por tipo y reporta `ok=false` para un tipo que nadie registró. Construir y conectar un conector ocurre después, a través de la factory que devolvió.

**Sin él:** `engine.New` devuelve un error de validación con el mensaje `connector registry is required`. No obtienes ningún valor de Engine, así que la extracción es imposible.

## CredentialProtector

***

**Obligatorio: solo con `WithEncryptedPersistence(true)`.**

La interfaz define `Protect`, que devuelve los bytes protegidos más la versión de clave que los protegió, y `Reveal`, que permite a un adaptador del host descifrar con una versión de clave dada.

Con la persistencia cifrada activada y una contraseña suministrada, el Engine llama a `Protect` y almacena el sidecar protegido. `Reveal` está disponible para los adaptadores del host; el núcleo del Engine no lo invoca. Tu host es dueño de la derivación, la rotación y el almacenamiento de claves, y el Engine registra solo la versión de clave devuelta por `Protect` como metadato libre de secretos.

**Sin él:**

* Con la persistencia cifrada **activa**, `engine.New` falla con `credential protector is required when encrypted persistence is enabled`. El Engine se niega a construirse antes que persistir credenciales en texto plano.
* Con la persistencia cifrada **inactiva**, el puerto es genuinamente opcional, y el Engine no pasa una contraseña suministrada al `ConnectionStore`.

## ConnectionStore

***

**Obligatorio: opcional en la construcción, imprescindible en tiempo de ejecución.**

El almacén persiste y resuelve descriptores de conexión que pertenecen a un tenant. Es el único punto de persistencia que usan las operaciones de conexión — el Engine no embebe MongoDB, ni SQL, ni ningún repositorio del host. Expone nueve operaciones: crear, buscar, buscar-por-id, actualizar, actualizar-por-id, borrar, borrar-por-id, listar y listar-paginado.

Tu implementación carga con dos obligaciones. **Debe** limitar cada registro por el tenant ID, para que un tenant nunca vea las conexiones de otro. **No debe** devolver material secreto — el descriptor de conexión no lleva ninguno.

**Sin él:** `engine.New` tiene éxito, y luego casi todo falla. Toda operación que toca una conexión devuelve el error de validación `connection store is not configured`. Eso cubre las nueve operaciones de conexión más planificar, ejecutar, descubrir esquema, descubrimiento de esquema fresco, validar esquema y probar conexión. `Limits()`, `AuthorizeConnectionAccess()` y `CheckActiveExecutions()` siguen disponibles; la última usa su checker opcional o es un no-op si no hay uno configurado.

<Warning>
  No leas `ConnectionStore` como una comodidad limitada al CRUD. Omitirlo también desactiva la extracción y el descubrimiento de esquemas.
</Warning>

## ExecutionStore

***

**Obligatorio: opcional.**

El almacén hace upsert del estado del ciclo de vida de una ejecución para un tenant. El Engine escribe las transiciones de forma síncrona e inline: `running`, y luego `completed`, `failed` o `canceled`.

Esas escrituras son **best-effort por diseño**. El Engine descarta un error de guardado, así que una persistencia opcional nunca puede corromper un resultado de extracción. Un fallo de escritura en el result sink se comporta distinto y sí hace fallar la ejecución. Los dos son deliberadamente distintos.

**Sin él:** el Engine corre sin seguimiento durable de ejecuciones, y tu host es dueño del estado de ejecución por fuera.

## ResultSink

***

**Obligatorio: opcional. Selecciona el modo de resultado.**

El sink persiste payloads de resultado en el storage que gestiona el host. La extracción en modo Store llama a `OpenResultStream`, así que el Engine escribe el resultado de forma incremental, en memoria constante. `PersistResult` sigue disponible para escrituras del payload completo.

La forma del stream es NDJSON contractual — un objeto JSON por línea, terminada en salto de línea, sin array que lo envuelva:

```json theme={null}
{"config":"<configName>","table":"<qualifiedTable>","row":{"<col>":"<val>"}}
```

Las líneas salen en orden canónico: los pasos por ordinal ascendente del plan, y las filas dentro de un paso en orden de cursor. El digest SHA-256 cubre exactamente los bytes escritos por esa corrida; dos corridas producen el mismo NDJSON y el mismo digest solo cuando los datasources devuelven las filas en un orden estable.

Ante un aborto — un error de escritura, un límite de tamaño superado o un contexto cancelado — el Engine abandona el escritor y nunca llama a `Close`. Trata un escritor sin cerrar como una escritura descartada, porque un resultado parcial nunca debe convertirse en una referencia devuelta.

**Sin él:** el modo Store no está disponible. El modo `auto` por defecto se resuelve a Direct, así que la extracción devuelve bytes inline y no persiste nada. Una petición explícita de modo Store falla de entrada con `store mode requires a configured result sink`, antes de que el Engine construya ningún conector.

## SchemaCache

***

**Obligatorio: opcional.**

La caché guarda y devuelve snapshots de esquema por tenant y por nombre de configuración.

El Engine la trata como un acelerador, nunca como fuente de verdad. Una lectura de caché fallida degrada a descubrimiento fresco. Una escritura de caché fallida igual devuelve al llamador el esquema descubierto. La llamada de descubrimiento siempre-fresco ignora la caché en cada invocación, incluso cuando conectaste una, para que se mantenga el contrato de datasource en vivo del endpoint de esquemas del Manager.

**Sin él:** el Engine descubre el esquema en vivo desde el datasource en cada llamada.

## ActiveExecutionChecker

***

**Obligatorio: opcional.**

El verificador reporta si una conexión tiene ejecuciones activas en este momento. `UpdateConnection` y `DeleteConnection` lo consultan antes de mutar una conexión. `UpdateConnectionByID` y `DeleteConnectionByID` deliberadamente no lo hacen; un host que use esas operaciones debe llamar a `CheckActiveExecutions` con el nombre de configuración resuelto antes de mutar.

El puerto es deliberadamente **lógico**, no un almacén durable de jobs. Tu host decide cómo responder: un repositorio de jobs, un rastreador en memoria, un lock distribuido o siempre falso. El Engine nunca importa un repositorio de jobs para hacer la pregunta. La identidad de conexión que pasa es el nombre de la configuración dentro del alcance del tenant. Tu respuesta **debe** estar limitada por tenant, para que el trabajo en curso de un tenant nunca bloquee la mutación de otro.

**Sin él:** el Engine no hace control de conflictos, y las actualizaciones y los borrados de conexiones proceden sin condiciones.

## Observability

***

**Obligatorio: opcional.**

El contrato tiene un método. `StartSpan` toma un contexto y un nombre de operación, y devuelve un contexto derivado más una función de cierre que el Engine difiere. Un solo método es justamente el punto: el núcleo del Engine nunca importa una biblioteca de trazas, y tu host adapta su propio tracer detrás del punto de extensión.

**Sin él:** la creación de spans devuelve el contexto entrante y una función de cierre no-op. Los hooks de trazas desaparecen, sin ningún otro cambio de comportamiento.

## Próximos pasos

***

<CardGroup cols={2}>
  <Card title="Embeber el Engine" icon="code" href="/es/fetcher/fetcher-embedding-the-engine">
    Importa, provee los puertos y construye con un ejemplo ejecutable.
  </Card>

  <Card title="Visión general del Engine" icon="cube" href="/es/fetcher/fetcher-engine-overview">
    El modelo de tres capas, la frontera de importación y los dos modos de resultado.
  </Card>
</CardGroup>
