engine.New. No infrastructure ships with the import. You wire only the parts your host application actually uses.
1. Install
The Engine is a distinct Go module from the Fetcher services (
github.com/LerianStudio/fetcher/v2). It carries zero third-party dependencies and has its own version line with path-prefixed tags (pkg/engine/vX.Y.Z). The import gives your module graph none of the service dependencies.2. Provide the ports
The Engine depends only on host-provided interfaces. One port is always required. A second is required only when encrypted persistence is on. The rest are opt-in, and the Engine degrades gracefully without them.
The port reference documents each contract and each degradation in full. Read it before you decide which ports to skip — “optional” does not mean “harmless”.
For tests and first runs, the
pkg/engine/memory harness provides in-memory implementations of the storage-facing ports: the connector registry, the connection store, the schema cache, the result sink, and the execution store. You need no MongoDB, Redis, RabbitMQ, or object storage to exercise the Engine. The harness ships no CredentialProtector, so a test that turns encrypted persistence on must supply one.
3. Construct, plan, execute
This example is self-contained. It uses the in-memory harness, so it runs with zero infrastructure.
What the example shows
- One required option.
WithConnectorRegistryis the only portengine.Newinsists on. The connection store here is a convenience, but skip it and every operation fails. - Construction-time validation.
engine.Newrejects a port passed as a typed nil, not only a literal nil. A misconfigured host fails at construction instead of panicking at first use. - Tenant scope on every call.
NewTenantContextbuilds the only isolation dimension the Engine knows. It carries a tenant ID and an optional request ID — no organization and no product. - Plan, then execute.
PlanExtractionvalidates the request against the live schema and applies the limits.ExecuteExtractionreads the plan. - Mode by composition. The example wires no
ResultSink, so the Engine picks direct mode and returns the bytes inline with a SHA-256 digest. Add a sink and the same code returns a storage reference instead. See Direct mode and Store mode.
Moving to production
Swap the memory harness for real adapters. Fetcher’s own services are the reference implementation, and their source is public. They bridge the Engine ports to real infrastructure under
pkg/enginecompat:
Read how the services wire them:
- Connection CRUD —
components/manager/internal/bootstrap/connection_engine.go - Schema discovery and caching —
components/manager/internal/bootstrap/schema_engine.go - Plan and execute extraction —
components/worker/internal/bootstrap/extraction_engine.go
- Your adapters own tenant scope. The Engine passes a tenant context to every port call and enforces the boundary at its own edge. A store that ignores the tenant ID leaks data across tenants, and the Engine cannot catch that for you.
- Your adapters own secrets. The Engine calls
ProtectandRevealand records only the returned key version as metadata. Key derivation, rotation, and storage stay in your host.
Next steps
Port reference
Every port, its contract, and the behavior you get without it.
Engine overview
The three-layer model, the import boundary, and the two result modes.

