Content-Encoding: gzip).
Source Config
The table config tells Ingest Engine what port to listen on, which path to accept requests at, how large requests can be, and how much to buffer before dropping events.port(required,u16) - Port number the embedded HTTP server binds to. The Ingest Engine worker listens on this port locally. To expose it externally, use a Kubernetes Service/Ingress or Docker port mapping. There is no default - you must specify it.path(String, default"/") - HTTP endpoint path where POST requests are accepted. Requests to any other path receive a 404. Use this to namespace endpoints when running multiple HTTP source pipelines on different paths behind a shared ingress, e.g./webhooks/stripeor/events/clickstream.bind_address(String, default"0.0.0.0") - Network interface to bind to.0.0.0.0listens on all interfaces (the right choice for containers and production). Use127.0.0.1to restrict to localhost during local development.buffer_size(usize, default1000) - Capacity of the in-memory channel between the HTTP handler and the pipeline processing loop. Each accepted request occupies one slot. When the buffer is full, new requests are rejected with HTTP 429 Too Many Requests so clients can back off (see Response Codes). The default of 1000 assumes roughly 100 KB payloads, targeting around 100 MB of memory usage. Increase for smaller payloads, decrease for larger ones. This value also controls the concurrency semaphore - at mostbuffer_sizerequests are processed concurrently.max_body_size(usize, default5242880/ 5 MB) - Maximum allowed request body size in bytes. Requests exceeding this limit are rejected with 413 Payload Too Large by the Axum body limit layer before the handler ever sees the data. Set this based on your expected payload sizes to protect the worker from memory pressure caused by unexpectedly large requests.auth(optional) - Authentication configuration. When set, every request must include a validAuthorizationheader. Unauthenticated requests receive 401 Unauthorized. See Authentication below.service_name(String, default"") - An identifier used to generate the Kubernetes Service resource name for this HTTP source. When set, the controller creates a Kubernetes Service namedhttp-src-{service_name}so that other services in the cluster can route traffic to this pipeline. If left empty, the default naming applies. Only relevant for Kubernetes deployments.
Authentication
The HTTP source supports optional authentication to protect the endpoint. When configured, every incoming request must carry a validAuthorization header - requests without one, or with incorrect credentials, receive a 401 Unauthorized response and the body bytes are tracked in the laminar_worker_http_source_bytes_rejected_total metric (label reason).
Bearer Token - the simplest option. The client sends Authorization: Bearer <token> and Ingest Engine does a constant-time string comparison:
Authorization: Basic <base64(username:password)>. Ingest Engine base64-decodes the header and compares the username:password string:
Parallelism Constraint
Parallelism must be 1. The HTTP source binds to a specific TCP port, and only one OS process can bind to a given port at a time. If the pipeline is configured with parallelism greater than 1, the operator panics at startup with a clear error message. This is enforced in theon_start hook - Ingest Engine checks ctx.task_info.parallelism and aborts before attempting to bind.
If you need to scale HTTP ingestion throughput beyond what a single job can handle, use a Pipeline Replica Set, which runs multiple independent replicas of the pipeline behind one load-balanced endpoint. Alternatively, use a message broker (Kafka, Redpanda) as a fan-out layer.
Relay and Replay
Unlike Kafka or CDC, an HTTP producer has no durable offset to rewind to. If the source is restarting, recovering, or shedding load (returning 429/503), any data pushed during that window is simply lost - the producer would have to retry it itself. The relay and replay services close this gap, giving HTTP ingestion broker-like durability without introducing a message broker. Both are optional: a deployment that can tolerate dropped requests, or that already fronts the source with its own queue, can skip them. Relay is an HTTP reverse proxy you place in front of the source pods. It forwards each request to the source and, whenever the source returns a 5xx/429 or is unreachable, transparently buffers the request body to object storage (S3/GCS/Azure) and still returns202 Accepted to the producer. Producers never observe the outage. The relay discovers which pipelines to buffer for automatically - the controller labels each HTTP source’s Kubernetes Service so the relay can find it.
Replay is the catch-up half. It runs as a periodic job (on a cron schedule rather than as a long-lived daemon): each pass reads the buffered objects back and re-POSTs them to the source once it has recovered, then exits. To avoid racing the relay as it writes the current buffer, replay stays a couple of minutes behind real time, and it checkpoints its progress per topic so an interrupted pass resumes where it left off. Delivery is at-least-once - if your pipeline needs to deduplicate replayed records, do so using a content-derived key.
Together they form a durable buffer in front of the source: the relay absorbs and persists traffic during outages, and replay drains that buffer back into the pipeline after recovery.
Schema
The schema block defines the expected structure of incoming request bodies. It is required - the HTTP source connector will not start without it.Format
Theformat field determines how request bodies are deserialized into typed Arrow columns:
json: {}- JSON (the most common choice for HTTP payloads)avro: {}- Apache Avroprotobuf: {}- Protocol Buffersparquet: {}- Apache Parquetraw_string: {}- Raw text, no parsingraw_bytes: {}- Raw binary, no parsing
Framing
By default, each HTTP request body is treated as a single record. If you want to send multiple records in one request, configure framing to split the body into individual records before deserialization:newline_delimited framing, the body is split on newline characters. Each line is deserialized independently. This works well with newline-delimited JSON (NDJSON):
Bad Data Handling
Thebad_data field controls what happens when a record fails deserialization:
fail: {}(default) - The pipeline stops with an error. Use this when data quality is critical and you want to catch schema mismatches immediately.drop: {}- The bad record is silently dropped and a warning is logged (rate-limited to avoid log flooding). The pipeline continues processing subsequent records. Use this when you expect occasional malformed data and prefer availability over completeness.
Sending Data
Send data to the running pipeline with any HTTP client. The only requirement is a POST request to the configured path with a body matching the expected format. Single record:Content-Encoding: gzip header is sufficient.
Response Codes
The HTTP source returns truthful status codes: 200 on success, 429 when overloaded, and 503 when the pipeline is shutting down. The handler stays non-blocking - the response is sent as soon as the request is enqueued or rejected, without waiting for downstream processing - but clients and load balancers can now back off correctly when the engine sheds load.
Clients should treat 429 and 503 as retryable load-shedding signals. For monitoring rejection rates and buffer pressure in production, use the Prometheus metrics
laminar_worker_http_source_events_dropped_total and laminar_worker_http_source_buffer_messages.