> ## 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, las credenciales llegan al almacén sin protección. |
| `ConnectionStore`        | Opcional                 | Toda operación excepto `Limits()` falla.                                                                                 |
| `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)`.**

El protector cifra y descifra material de credenciales para un tenant. La llamada `Protect` devuelve los bytes protegidos más la versión de clave que los protegió. La llamada `Reveal` descifra con una versión de clave dada, así que tu host puede resolver una clave rotada.

Tu host es dueño de la derivación, la rotación y el almacenamiento de claves. El Engine solo llama al punto de extensión y registra la versión de clave devuelta 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 las credenciales llegan al `ConnectionStore` sin protección.

## 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. Un Engine sin almacén de conexiones solo puede reportar sus límites.

<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 determinista: los pasos por ordinal ascendente del plan, y las filas dentro de un paso en orden de cursor. Por eso la misma entrada produce un NDJSON idéntico byte a byte y el mismo digest SHA-256 en cada corrida.

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. El Engine lo consulta **antes** de mutar una conexión. Así un host puede mantener el comportamiento del Manager, que bloquea un cambio mientras corren jobs contra esa conexión.

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>
