Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Endpoints (connectors)

An endpoint is a source (consumer) or sink (publisher) for messages. Every endpoint — whatever the underlying protocol — is driven behind the same receive_batch / send_batch shape, so any source can feed any sink. In config, an endpoint is a single-key object naming the connector:

input:  { kafka:   { url: "localhost:9092", topic: "orders" } }
output: { mongodb: { url: "mongodb://localhost:27017", database: "app", collection: "orders" } }

This page catalogs the transport endpoints and their behaviour. For per-connector query parameters used by copy (name, type, default, required, description), see the generated URL parameter reference and the hand-written connector pages. Structural endpoints (ref, fanout, switch, response, …) are documented separately in Structural endpoints.

Supported transports

Kafka, NATS, AMQP (RabbitMQ), MQTT, MongoDB, Postgres CDC (logical replication), PostgreSQL / MySQL / SQLite / MariaDB (SQLx), ClickHouse, HTTP, WebSocket, gRPC, ZeroMQ, Redis Streams, AWS SQS/SNS, cloud object storage (S3 / GCS / Azure), IBM MQ, files, and in-memory channels (memory, in-process and cross-process IPC).

Consumer vs. subscriber, and nack support

Endpoints default to a Consumer pattern (a queue: messages are distributed among workers). To get Subscriber (pub/sub) behaviour, per-backend config is required.

BackendSubscriber Config (Pub/Sub)Request-ReplyNack Support
AMQPSet subscribe_mode: trueEmulated (Property)Yes (Basic.nack)
AWSN/A (Use SNS)NoYes (Visibility Timeout)
FileSet mode: subscribeNoSimulated (In-Memory)
gRPCN/ANoNo
HTTPN/ANative (Implicit)Yes (HTTP 500)
IBM MQSet topicNoYes (Tx Rollback)
KafkaOmit group_idEmulated (Header)Eventual (Skip Offset)
Memory (in-process)Set subscribe_mode: trueEmulated (Metadata)Yes (Re-queue), by default disabled
Memory (IPC: ipc://, unix://, pipe://)Not supportedNot supportedYes (Re-queue), by default enabled, consumer-local
MongoDBSet consume: capture_new (replica set)Emulated (Metadata)Yes (Unlock)
MQTTSet clean_session: trueEmulated (Property)Eventual (Skip Ack)
NATSSet subscriber_mode: trueNative (Inbox)Yes (JetStream Nak)
Postgres CDCN/A (streams committed changes)NoYes (confirmed LSN not advanced)
Redis StreamsSet subscriber_mode: trueNoEventual (PEL, un-acked)
SledSet delete_after_read: falseNoYes (Tx Rollback)
SQLxNot supportedNoEventual (Skip Delete)
WebSocketN/ANoNo
ZeroMQSet socket_type: "sub"Native (REQ/REP)No
  • Request-ReplyNative uses protocol-level correlation (HTTP connection, NATS reply subject). Emulated publishes a new message to a reply destination (the reply_to metadata field) carrying a correlation_id.
  • NackYes is explicit negative acknowledgement triggering redelivery. Eventual means redelivery depends on timeout or a connection drop. Simulated is handled in-memory. Consumer-local means the nacked message is retried inside the consumer process and is lost if that process dies — the producer is never notified.

Database sources: change capture vs. polling

Databases have no native pub/sub, so a database source is read one of two ways:

  • Change Data Capture (CDC) tails the database’s own change log — inserts and updates/deletes — and resumes from a durable log position.
    • PostgreSQL — the postgres_cdc endpoint streams a logical-replication slot (pgoutput). It emits flat JSON rows tagged with postgres.operation metadata (insert/update/delete/truncate). Acking a batch confirms the LSN back to the server; a nack or interrupted run does not advance the confirmed LSN, so replication resumes from the last acknowledged position — at-least-once. Enable with the postgres-cdc feature; requires wal_level = logical and a publication. See the Postgres CDC → JSONL tutorial.
    • MongoDBconsume: capture_all (the default) reads existing documents first and then keeps capturing (no gap, at-least-once); consume: capture_new watches an existing collection for changes from now on. Both emit insert/update/replace/delete tagged with mongodb.operation and checkpoint under cursor_id. Change streams need a replica set; without one, use consume: snapshot for a one-shot read.
  • Cursor polling pages an existing table by a monotonic cursor_column (WHERE col > $last ORDER BY col ASC), persisting the last read value under cursor_id. Captures appends only — updates and deletes are not observed. Available on SQLx (PostgreSQL / MySQL / MariaDB / SQLite) and ClickHouse; the poll interval backs off exponentially between polling_interval_ms and max_polling_interval_ms. SQLite and ClickHouse are polling-only.

SQLx read mode is chosen by config, not by driver. With no cursor_column, an SQLx source is a competing-consumers work queue: it atomically claims rows via a locked_until lease and deletes them on ack. This requires the table to carry the queue schema — an id, a payload, and a locked_until column — on every driver; it is not a plain full-table read. To read an arbitrary table non-destructively (the ETL path), set cursor_column to switch to cursor polling. A source table missing locked_until fails fast with a permanent error rather than reconnecting forever.

MongoDB consume modes

consumemechanismmodifies sourceends on drainneeds a replica setuse for
capture_all (default)_id snapshot, then change streamnoyes — when the stream goes quietyesbulk read / ETL
capture_newchange stream, new changes onlynono — continuous by natureyesongoing CDC
snapshotpages the collection by _id, one shotnoyesnoreading a standalone mongod
consumerclaim → lock → re-fetch → deleteyesyesnowork queues, competing readers

These are not interchangeable. consumer buys exclusivity via four round trips per batch and deletes what it reads, so it is for work queues, not for reading a collection; where you need a single-reader bulk read or ETL pass, the default capture_all is roughly 5x faster (500k docs: consumernull 23,667 rows/s vs. capture_all → jsonl 120,308 rows/s). Note concurrency does not speed up a MongoDB source; batches are fetched serially.

Both capture_* modes read the oplog and therefore require a replica set — a single-node one is enough — and refuse to start without one. On a standalone mongod, use snapshot for a one-shot non-destructive read. snapshot delivers what exists when the run starts and is not a tail: it pages by _id, which is assigned client-side and so does not follow commit order, so it rejects cursor_id rather than resuming above a stored _id and silently skipping whatever a concurrent writer commits below that mark. Incremental reads need commit order — i.e. the oplog, i.e. a replica set.

Removed in 0.4.0: consume: subscriber. It polled seq > last_seq and advanced its watermark to the highest seq it had seen, so a batch whose seq block was reserved first but committed second was skipped permanently — silent loss, with no error and no gap in the delivery count. Use capture_new for ephemeral fan-out (it now reads arbitrary collections, not only documents written by the bridge’s own publisher). The deprecated change_stream: true boolean resolves to capture_new.

Cloud object storage (S3 / GCS / Azure)

The object_store endpoint (alias s3) reads and writes Amazon S3, Google Cloud Storage, Azure Blob, Cloudflare R2, and anything else the object_store crate speaks. Enable with the object-store feature. Credentials and backend options come from the environment (AWS_ACCESS_KEY_ID, AWS_REGION, GOOGLE_SERVICE_ACCOUNT, AZURE_STORAGE_ACCOUNT, …); the URL scheme picks the backend (s3://, gs://, az://).

  • As a sink, each flushed batch is encoded with the file formats (normal JSONL, json, text, raw) and written as one immutable object at <prefix>/[YYYY/MM/DD/]<uuidv7>.<ext>. Objects are write-once.
  • As a source, objects under the prefix are listed in key order, fetched whole, split on the delimiter, and emitted. Progress is a durable cursor holding the last fully-acked object key: set cursor_id and an external checkpoint_store (file://, s3://, postgres://, mongodb://) so a restart resumes. Objects are never deleted or rewritten.
archive_to_s3:
  input:  { memory: { topic: "events" } }
  output: { object_store: { url: "s3://my-bucket/events", format: normal } }

replay_from_s3:
  input:
    object_store:
      url: "s3://my-bucket/events"
      cursor_id: "replayer-1"
      checkpoint_store: "file:///var/lib/mqb/s3-cursor.json"
  output: { nats: { subject: "events.replay", url: "nats://localhost:4222" } }

Point checkpoint_store at a different bucket/prefix than the source reads; a cursor object under the source prefix would be listed and re-read as data.

File and object formats

Both file and object_store share the encodings: normal/json/text write the {message_id, payload, metadata} wrapper (the message id survives the round trip); raw writes payloads verbatim (bare documents, no wrapper). csv is supported as a source (and on file sinks). The file endpoints also carry their own compression and encryption fields — see the Compression and Encryption at rest recipes.

Memory endpoint and IPC

The memory endpoint is both an in-process channel and a cross-process IPC transport over Unix domain sockets / Windows named pipes. Its topic field (alias url) doubles as a transport URL. See the Cross-process IPC bridge tutorial and Learn the architecture for the URL schemes, roles (consumer is server, publisher is client), wire format and backpressure.

IBM MQ

IBM MQ is included in the full feature set via the ibm-mq feature, which loads the IBM MQ client library at runtime via dlopen — no IBM SDK is needed to build. The redistributable client only has to be present at runtime, and only if you actually use an IBM MQ endpoint (it is loaded lazily on first connect). The loader finds the client via the platform default name, MQ_INSTALLATION_PATH (e.g. /opt/mqm), or an explicit MQB_IBM_MQ_LIB path. To link statically at build time, use ibm-mq-static (requires the IBM MQ SDK). IBM MQ has its own TLS shape (IbmTlsConfig): tls.cert_file (alias key_repository) is a CMS key repository path, not a PEM file. See the IBM MQ setup guide.

Connection sharing

Publishers that target the same server reuse one underlying transport client by default, consolidating TCP connections, background threads, and batching. Sharing applies to Kafka, NATS, MongoDB, SQLx, HTTP, and gRPC; the client is keyed by connection-level settings (URL, auth, TLS, client options), never by topic/subject/collection. Set shared: false on a publisher to give it a dedicated connection — see Configuration grammar and Performance tuning.

Idempotent writes

For ETL, at-least-once delivery plus an idempotent write gives effective exactly-once. The most robust place to enforce this is the sink’s own unique constraint. See the Upserts & insert-if-absent and Deduplication recipes for the per-sink mechanisms (id_field on MongoDB, ON CONFLICT/ON DUPLICATE KEY on SQL, ReplacingMergeTree on ClickHouse, deterministic message_id + postgres.key on Postgres CDC).