Skip to main content
Every extraction job ends in one terminal event. The Worker publishes job.completed when the result reaches storage, and job.failed when the run stops. Subscribe to those events instead of polling GET /v1/fetcher/{id}. This page covers the consumer side: what arrives, what it means, and what to do with it.

Where the events arrive


The Worker publishes to a durable topic exchange. RABBITMQ_JOB_EVENTS_EXCHANGE names it, and the shipped value is fetcher.job.events. Bind your own queue to the routing keys you care about. The local infrastructure definition binds two queues as an example: fetcher.job.completed.queue and fetcher.job.failed.queue.
Job events are a product contract, not an optional feature. The Worker refuses to start without streaming enabled and without an exchange name. See Deployment for the operator side.

job.completed


This event means one thing: the extraction ran to the end, and the encrypted result now sits in object storage. The Worker publishes it after two earlier steps. First it writes the result object, then it records the terminal status on the job. Only then does it emit the event.
metadata carries the metadata you sent on the job, so source and any correlation identifier come back to you unchanged. sizeBytes and rowCount describe the plaintext result, before encryption. path is the object key of the stored result.

job.failed


This event means the run stopped and Fetcher stored no result. Extraction is fail-fast, so the first datasource that fails ends the whole job. A partial result never reaches storage. The event fires for any failure along the path: an unresolvable connection name, a datasource error, a schema mismatch, or a storage write that did not complete.
A failed event carries no result block and no completedAt. metadata.error.message passes through a redaction step first. Fetcher replaces four leak shapes with [redacted]: connection URIs, the address operand of a Go network error, the Addr: operand of a MongoDB driver error, and an IPv4 literal. The rest of the text survives, so the message stays actionable. Route it to operators.

The CloudEvents envelope


Every message travels in CloudEvents binary mode, version 1.0. The context attributes ride as AMQP headers. A single-tenant deployment still carries a tenant value. It emits the literal single-tenant, so one consumer handles both deployment shapes with the same code.

CloudEvents source configuration


STREAMING_CLOUDEVENTS_SOURCE has no default value. The Worker requires it whenever streaming is on, and it stops at startup when the value is empty. The shipped example uses //lerian.fetcher/worker. Fetcher copies the value into ce-source verbatim. Give each Worker deployment its own source value when several producers share one broker, and route on that header.

Delivery contract


Delivery is at-least-once. Deduplicate on ce-id.
  • The Worker writes the event to a durable outbox before it publishes. A broker outage delays the event, it does not lose it.
  • A repairer scans for terminal events that never published and re-emits them every 30 seconds.
  • ce-id stays identical across every re-emission of the same job and status. Nothing else is stable enough to key on.
  • Order is not a guarantee. Two jobs can complete in one order and arrive in another.
  • The Worker records the terminal job status before it emits. GET /v1/fetcher/{id} remains the authority on job state.
Treat a repeated ce-id as a duplicate and acknowledge it without reprocessing. A consumer that keys on the message identifier of the broker will process the same job twice.

Verifying what you receive


Two independent signatures protect the path between the Worker and you. Both use HMAC-SHA256 with the external HMAC key. HKDF-SHA256 derives that key from the APP_ENC_KEY master key. The Fetcher repository ships a small tool that prints it, so a consumer verifies signatures without ever holding the master key. The message. The Worker signs each published message and stamps three headers on it: x-message-signature, t for the signing timestamp, and signature-version. The signed payload binds the timestamp, the signature version, the tenant, the job identifier, the exchange, and the routing key, followed by the message body. Replaying the same body under another tenant or another route fails verification. The result. result.hmac is the keyed HMAC-SHA256 over the plaintext result JSON, computed before encryption. The integrity block states the same value with its algorithm. Verify it after you decrypt and before you trust the rows. The protection block describes the stored bytes: encrypted is true, the storage adapter applied it, and the mode is adapter-managed. It describes the result only, never the datasource credentials.

Next steps


Extraction jobs

What a job requests, and the states it moves through.

Fetcher REST API

Create a job, read a job, and manage connections.

Configuration

The streaming, exchange, and encryption variables behind these events.

Architecture

The Manager, the Worker, and the Engine they both run over.