rust-camel
A Rust-native, Tower-native integration framework inspired by Apache Camel, built for async pipelines, EIP patterns, and production observability.
Status: Pre-release (
0.24.0). APIs will change.
Overview
rust-camel lets you define message routes between components using a fluent builder API. The data plane (exchange processing, EIP patterns, middleware) is Tower-native — every processor and producer is a Service<Exchange>. The control plane (components, endpoints, consumers, lifecycle) uses its own trait hierarchy.
Current components: timer, cron, log, direct, exec, mock, file, http, ws/wss, kafka, mqtt, redis, sql, jms, cxf, container, controlbus, validator, xslt, xj, master, opensearch, llm, surrealdb, grpc, seda, keycloak, wasm.
Architecture
rust-camel separates two planes:
- Data plane — Tower-native. Every processor and producer is a
Service<Exchange>. EIP patterns compose as Tower middleware. - Control plane — its own trait hierarchy:
Component,Endpoint,Consumer, route lifecycle, supervision, hot-reload.
┌──────────────────────────────────────────────────────┐
│ Your Application │
│ RouteBuilder / YAML DSL / camel-config │
└──────────────────────┬───────────────────────────────┘
│
┌──────────────────────▼───────────────────────────────┐
│ camel-core │
│ CamelContext — composition root │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Data plane (Tower) │ │
│ │ Exchange → Service<Exchange> pipeline │ │
│ │ EIP processors as Tower middleware │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Control plane │ │
│ │ Route lifecycle, supervision, hot-reload │ │
│ │ RuntimeBus (CQRS), event journal (redb) │ │
│ └──────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
│ │
┌─────────▼──────────┐ ┌───────────▼────────────────┐
│ camel-processor │ │ Components │
│ EIP patterns │ │ timer, log, http, file, │
│ (Tower layers) │ │ kafka, redis, sql, opensearch, ..., │
└────────────────────┘ └────────────────────────────┘
The internal DDD/CQRS/hexagonal structure of
camel-coreis an implementation detail — seecrates/camel-core/README.mdfor those internals.
Runtime Bus
// Commands modify state
ctx.runtime.execute.await?;
// Queries read from projections (not controller)
let status = ctx.runtime_route_status.await?;
Optional Durability
// Enable redb-backed event journal for runtime state recovery
let ctx = builder
.runtime_store
.build
.await?;
Quick Start with the CLI
This scaffolds a project with a Camel.toml, routes/hello.yaml, and runs it.
See crates/camel-cli/README.md for all CLI commands.
# Generate OpenAPI document from REST route files
Docker
Pre-built images are available on GHCR and Docker Hub.
# Pull the scratch image (production)
# Pull the alpine image (debugging)
# Run with routes mounted
# Interactive shell (alpine)
# Docker Hub equivalent
Two image variants are published per release:
| Tag suffix | Base | Use case |
|---|---|---|
| (none) | scratch | Production. Minimal attack surface. |
-alpine |
alpine:3.21 | Debugging. Includes busybox shell. |
Both variants support linux/amd64 and linux/arm64.
Quick Rust Example
use ;
use RouteBuilder;
use LogComponent;
use TimerComponent;
use CamelContext;
async
REST DSL & OpenAPI
Define REST APIs declaratively in YAML with automatic JSON binding, path templates, schema validation, and OpenAPI document generation.
rest:
- host: 0.0.0.0
port: 8080
path: /api/users
operations:
- method: GET
operation_id: listUsers
to: direct:listUsers
produces: application/json
- method: POST
operation_id: createUser
consumes: application/json
produces: application/json
success_status: 201
to: direct:createUser
request_schema:
type: object
properties:
name:
type: string
email:
type: string
required:
- method: GET
path: /{id}
operation_id: getUser
to: direct:getUser
- method: DELETE
path: /{id}
operation_id: deleteUser
to: direct:deleteUser
success_status: 204
The rest: block lowers to http: consumer routes with:
unmarshal(json)for body verbs (POST/PUT/PATCH)- JSON schema validation when
request_schemais present (→ 400 on failure) marshal(json)+Content-Type: application/jsonon the response path- Default status codes (200/201/204) via
CamelHttpResponseCodeheader
Generate an OpenAPI 3.0.3 document from REST routes:
See examples/rest-crud/ for a complete
runnable example with in-memory CRUD storage and static file co-hosting.
Crate Map
| Crate | Description |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --- | ------------------------------------ |
| camel-api | Core types: Exchange, Message, Body, CamelError, BoxProcessor, ProcessorFn, RuntimeCommand, RuntimeQuery, RuntimeEvent, FromBody, impl_from_body_via_serde! |
| camel-core | Runtime engine with DDD/CQRS: CamelContext, domain aggregates, ports, adapters, event journal |
| camel-config | Configuration: CamelConfig, route discovery from YAML files with glob patterns |
| camel-builder | Fluent RouteBuilder API |
| camel-component | Component, Endpoint, Consumer traits |
| camel-processor | EIP processors: Filter, Choice, Splitter, StreamingSplitter, Aggregator, WireTap, Multicast, SetHeader, MapBody, Marshal/Unmarshal + Tower Layer types |
| camel-endpoint | Endpoint URI parsing utilities; UriConfig derive macro for typed component config |
| camel-endpoint-macros | Proc-macro crate backing #[derive(UriConfig)] |
| camel-wit | WIT interface definitions crate for camel components |
| camel-bean | Bean/Registry system for dependency injection and business logic integration |
| camel-bean-macros | Proc-macro crate for #[bean] attribute |
| camel-dsl | YAML DSL: load and run routes from .yaml files |
| camel-health | Health check types and endpoint support |
| camel-platform-kubernetes | Kubernetes-native platform SPI: leader election, readiness gates, pod identity |
| camel-timer | Timer source component |
| camel-cron | Cron source component — calendar-triggered scheduling via Unix 5-field cron expressions, backed by CronService SPI |
| camel-log | Log sink component |
| camel-direct | In-memory synchronous component |
| camel-mock | Test component with assertions on received exchanges (await_exchanges, ExchangeAssert) |
| camel-test | Integration test harness |
| camel-controlbus | Control routes dynamically from within routes |
| camel-validator | Validate body against JSON/YAML schemas, plus XSD via xml-bridge |
| camel-http | HTTP producer (client) and HTTP consumer (server, native streaming) |
| camel-file | File producer and consumer |
| camel-kafka | Kafka producer and consumer with SSL/SASL and manual commit |
| camel-mqtt | MQTT 3.1.1 producer and consumer (rumqttc-v4-next) with QoS 0/1/2, manual/auto ack, TLS via mqtts:// |
| camel-redis | Redis producer and consumer |
| camel-opensearch | OpenSearch producer with 7 operations (INDEX, SEARCH, GET, DELETE, UPDATE, BULK, MULTIGET) |
| camel-sql | SQL producer/consumer with IN clause separator, SSL/TLS, streaming result support |
| camel-surrealdb | SurrealDB multi-model (document, graph, vector, live) with 9 operations |
| camel-jms | JMS producer and consumer via native-image bridge (ActiveMQ Classic, Artemis) |
| camel-component-cxf | SOAP/Web Services via Apache CXF native-image bridge: SOAP 1.1/1.2, WSDL, WS-Security, multi-tenant profiles, PAYLOAD mode |
| camel-component-exec | Fail-closed system command execution with profile-pinned binaries, per-element arg-policy, env sanitization, cwd confinement, timeout with process-group kill (exec:{profile}) |
| camel-grpc | gRPC producer and consumer with dynamic proto resolution, unary + server/client/bidi streaming |
| camel-xslt | XSLT 3.0 transformation via xml-bridge (xslt:<stylesheet>) — example |
| camel-xj | XML↔JSON conversion via xml-bridge (xj:<stylesheet>?direction=xml2json\|json2xml) — example |
| camel-container | Docker container producer/consumer via bollard. Container lifecycle, volume mounts, exec, network operations |
| camel-component-llm | LLM chat (streaming + materialized), embeddings, tool calling, multi-turn conversations, response cache, cost observability, retry, and concurrency control via OpenAI, Ollama, or Mock. Strict siumai adapter boundary (ADR-0020) (llm:chat, llm:embed) |
| camel-language-api | Language trait API: Language, Expression, Predicate |
| camel-language-simple | Simple Language: ${header.x}, ${body}, operators, &&/` | | `, boolean literals, null semantics |
| camel-language-js | JavaScript scripting language for expressions and side effects |
| camel-language-rhai | Rhai scripting language for full expression power |
| camel-language-jsonpath | RFC 9535 JSONPath expressions: $.items[*].price. Requires lang-jsonpath feature. |
| camel-language-xpath | XPath 1.0 language for XML body queries: /books/book[1]/title. Requires lang-xpath feature. |
| camel-prometheus | Prometheus metrics exporter with /metrics endpoint |
| camel-otel | OpenTelemetry tracing and metrics exporter |
| [examples/rest-crud] | REST DSL + OpenAPI example — CRUD API with schema validation, static co-hosting |
Building & Testing
The benchmark suite uses Criterion with HTML reports. Per-crate inline benchmarks cover core types, processors, and the DSL. The camel-bench crate provides integration pipeline benchmarks.
Test Coverage
Requires cargo-llvm-cov. Coverage baseline is enforced via coverage.toml (currently 75% minimum). Adjust the baseline there if coverage changes intentionally.
Implemented EIP Patterns
| Pattern | Builder Method | Description |
|---|---|---|
| Aggregator | .aggregate(config) |
Correlate and aggregate exchanges with size/timeout completion, expression correlation, force-complete-on-stop |
| Content-Based Router | .choice() / .when() |
Route based on exchange content |
| Content Enricher | .enrich(uri) / .poll_enrich(uri, timeout) |
Merge additional content mid-route via producer (enrich) or polling consumer (pollEnrich); pluggable EnrichmentStrategy (default UseEnrichedBody) |
| Delayer | .delay() / delay: |
Fixed or dynamic delay (header-based) |
| doTry / doCatch / doFinally | .do_try() / do_try: |
Lexical-scope try/catch/finally with ADR-0019 dispositions |
| Dynamic Router | .dynamic_router(expr) |
Expression-based routing with slip pattern |
| Routing Slip | .routing_slip(expr) |
Route through a sequence of endpoints determined at runtime |
| Filter | .filter(predicate) |
Forward exchange only when predicate is true |
| Load Balancer | .load_balance() |
Distribute across endpoints with RoundRobin/Random/Weighted/Failover |
| Loop | .loop_count(n) / loop: |
Iterate a sub-pipeline N times or while a predicate holds true |
| Marshal / Unmarshal | .marshal(fmt) / .unmarshal(fmt) |
Serialize/deserialize bodies using pluggable data formats (JSON, XML, ZIP, CSV) |
| Multicast | .multicast() |
Send the same exchange to multiple endpoints |
| RecipientList | .recipient_list(config) |
Dynamically resolve endpoint URIs from an expression at runtime |
| Splitter | .split(config) |
Split one exchange into multiple fragments (body lines, ZIP entries, streaming) |
| Stream Cache | .stream_cache(n) / stream_cache: |
Materialize Body::Stream into Body::Bytes with configurable threshold (128 KB default) |
| Throttler | .throttle(n, duration) |
Rate limiting with Delay/Reject/Drop strategies |
| WireTap | .wire_tap(uri) |
Fire-and-forget copy to a tap endpoint |
Run an example:
Security Features
rust-camel includes production-ready security features:
SSRF Protection (HTTP Component)
// Block private IPs by default
from
.to
.build?
// Custom blocked hosts
from
.to
.build?
Path Traversal Protection (File Component)
All file operations validate that resolved paths remain within the configured base directory. Attempts to use ../ or absolute paths outside base are rejected.
Timeouts
All I/O operations have configurable timeouts:
- File:
readTimeout,writeTimeout(default: 30s) - HTTP:
connectTimeout,responseTimeout
Memory Limits
Aggregator supports max_buckets and bucket_ttl to prevent memory leaks.
Observability
Correlation IDs
Every exchange has a unique correlation_id for distributed tracing.
Metrics
Implement MetricsCollector trait to integrate with Prometheus, OpenTelemetry, etc.
Prometheus Metrics
Export metrics to Prometheus with automatic lifecycle management:
use PrometheusService;
let ctx = builder.build.await?
.with_lifecycle
.with_tracing;
ctx.start.await?;
// Prometheus server starts automatically
Available metrics:
camel_exchanges_total{route}- Total exchanges processedcamel_errors_total{route, error_type}- Total errorscamel_exchange_duration_seconds{route}- Exchange processing duration (histogram)camel_queue_depth{route}- Current queue depthcamel_circuit_breaker_state{route}- Circuit breaker state
Architecture: PrometheusService implements Lifecycle trait (following Apache Camel's Service pattern, adapted to avoid tower::Service confusion).
Health Monitoring
Built-in health endpoints for Kubernetes:
/healthz- Liveness probe/readyz- Readiness probe/health- Detailed health report
Platform SPI support for Kubernetes:
- Leader election via Kubernetes Leases
- Readiness gate via pod status conditions
- Pod identity auto-detection from Downward API
livenessProbe:
httpGet:
path: /healthz
port: 9090
Route Lifecycle Management
rust-camel supports controlling when and how routes start:
Auto Startup
By default, all routes start automatically when ctx.start() is called. You can disable this:
let route = from
.route_id
.auto_startup // Won't start automatically
.to
.build?;
Startup Order
Control the order in which routes start (useful for dependencies):
let route_a = from
.route_id
.startup_order // Starts first
.to
.build?;
let route_b = from
.route_id
.startup_order // Starts after route-a
.to
.build?;
Runtime Control
Control routes dynamically from code or from other routes:
// From code:
let runtime = ctx.runtime;
runtime.execute.await?;
runtime.execute.await?;
// From another route (using controlbus):
from
.set_header
.to
.build?
See examples/lazy-route for a complete example.
Type Converters
The pipeline automatically coerces the exchange body to the type a component endpoint declares via body_contract(). No manual casting needed in .process() closures.
// Declare what your endpoint expects (component author):
// Deserialize body into any type (route author):
use impl_from_body_via_serde;
use Deserialize;
impl_from_body_via_serde!;
from
.process
.to
.build?;
Built-in FromBody impls: String, Vec<u8>, Bytes, serde_json::Value.
Error Handling
rust-camel provides sophisticated error handling with retry policies and dead letter channels.
RedeliveryPolicy with Jitter
Configure retry behavior with exponential backoff and jitter:
use ;
use Duration;
let error_handler = dead_letter_channel
.on_exception
.retry // Max 3 retry attempts
.with_backoff
.with_jitter // ±20% randomization (recommended: 0.1-0.3)
.build;
Jitter Benefits:
- Prevents thundering herd in distributed systems
- Recommended values: 0.1-0.3 (10-30%)
- Adds randomization:
delay ± (delay * jitter_factor)
Camel-Compatible Headers
During retries, these headers are automatically set:
CamelRedelivered-truewhen exchange is being retriedCamelRedeliveryCounter- Current retry attempt (1-indexed)CamelRedeliveryMaxCounter- Maximum retry attempts
RouteBuilder shorthand
from
.route_id
.dead_letter_channel
.on_exception
.retry
.handled_by
.end_on_exception
.to
.build?;
YAML Configuration
routes:
- id: "retry-example"
from: "timer:tick"
error_handler:
dead_letter_channel: "log:dlc"
retry:
max_attempts: 3
initial_delay_ms: 100
multiplier: 2.0
max_delay_ms: 10000
jitter_factor: 0.2
on_exceptions:
- kind: "ProcessorError"
message_contains: "validation"
retry:
max_attempts: 1
steps:
- to: "direct:processor"
Exception Disposition — Propagate, Handled, Continued
Every on_exception clause has a disposition that controls what happens after the error handler runs:
| Disposition | Behavior |
|---|---|
Propagate |
Error is re-thrown to upstream after DLC/handler runs (default) |
Handled |
Error is absorbed as Ok(Exchange). Pipeline stops — subsequent steps do NOT run |
Continued |
Error is cleared from the Exchange. Pipeline continues to the next step — subsequent steps run normally |
In the builder API, use ErrorHandlerConfig with .continued(true) or .handled(true):
use ErrorHandlerConfig;
let eh = dead_letter_channel
.on_exception
.continued // ← clear error, pipeline continues
.retry
.build;
let route = from
.route_id
.error_handler
.to // ← runs even after the error
.build?;
In YAML, use the continued: true field:
error_handler:
dead_letter_channel: "log:errors"
on_exceptions:
- kind: ProcessorError
continued: true
The continued and handled fields are mutually exclusive — setting both to true is a compile error.
See examples/error-handling for complete examples, including Route 10 which demonstrates continued=true.
Configuration
rust-camel supports external configuration via Camel.toml files using the camel-config crate:
Configuration File
Create a Camel.toml file:
[]
= ["routes/**/*.yaml"]
= "INFO"
# Component defaults - apply to all endpoints
[]
= 5000
= false
[]
= "localhost:9092"
= "camel"
[]
= "localhost"
= 6379
[]
= "localhost"
= 9200
[]
= 5
[]
= 500
[]
= "unix:///var/run/docker.sock"
# Observability
[]
= true
= 9090
[]
= "ERROR"
[]
= "prod-kafka:9092"
[]
= "prod-redis"
Component Defaults
Each component supports global defaults that apply to all endpoints. URI parameters always take precedence:
// Uses global connect_timeout_ms (5000) from Camel.toml
.to
// Overrides global setting with URI parameter
.to
Supported component configurations:
[components.http]:connect_timeout_ms,response_timeout_ms,max_connections,max_body_size,max_request_body,allow_internal[components.kafka]:brokers,group_id,session_timeout_ms,request_timeout_ms,auto_offset_reset,security_protocol,partition_assignment_strategy. Named clusters under[components.kafka.brokers_named.<name>]each withbrokers, optionalsecurity_protocol,sasl_auth_type,client_id, andrdkafka_configescape hatch. Reference via?brokerName=<name>in the endpoint URI.[components.redis]:host,port[components.sql]:max_connections,min_connections,idle_timeout_secs,max_lifetime_secs,ssl_mode,ssl_root_cert,ssl_cert,ssl_key[components.jms]:default_broker,max_bridges,bridge_cache_dir,bridge_start_timeout_ms,broker_reconnect_interval_ms. Brokers are declared as named entries under[components.jms.brokers.<name>], each withbroker_url,broker_type(activemq|artemis), and optionalusername/password. URI schemesactivemq:andartemis:lock the broker type automatically and support shorthand destinations (e.g.activemq:orders→ queue). Use thebroker=<name>URI query param to select a specific broker from the pool.[components.file]:delay_ms,initial_delay_ms,read_timeout_ms,write_timeout_ms[components.container]:docker_host[components.ws]:max_connections,max_message_size,heartbeat_interval_ms,idle_timeout_ms
Each optional component (http, ws, kafka, redis, sql, jms, file, container) implements ComponentBundle — it owns its config key, deserializes its own TOML block, and registers one or more schemes. See examples/custom-component-bundle for a full walkthrough of implementing your own bundle.
Loading Configuration
use CamelError;
use LogComponent;
use TimerComponent;
use ;
use CamelContext;
// Load configuration
let config = from_file
.map_err?;
// Create context and register components
let mut ctx = builder.build.await?;
ctx.register_component;
ctx.register_component;
// Discover and load routes from config patterns
let routes = discover_routes
.map_err?;
for route in routes
ctx.start.await?;
Route Files
Create YAML route files:
# routes/hello.yaml
routes:
- id: "hello-timer"
from: "timer:tick?period=1000"
steps:
- to: "log:info"
Environment Variables
Override configuration with environment variables:
# Select profile
# Override specific values
Features
- Profile support: Multiple environments in one file
- Route discovery: Auto-load routes from glob patterns
- Component defaults: Set global defaults for HTTP, Kafka, Redis, SQL, File, Container
- Environment overrides: Override any value with
CAMEL_*prefix - Deep merging: Nested configs merge properly
- URI precedence: URI parameters always override global defaults
Component Defaults
Configure global defaults for all component endpoints in Camel.toml:
[]
= 5000
= false
[]
= "localhost:9092"
= "my-app"
[]
= "localhost"
= 6379
[]
= 10
[]
= 1000
[]
= "unix:///var/run/docker.sock"
URI parameters always take precedence over global defaults:
// Uses global connect_timeout_ms (5000) from Camel.toml
.to
// Overrides global setting with URI parameter
.to
See docs/configuration.md for full details.
JSON Schema
The full DSL AST has a published JSON Schema:
- URL:
https://raw.githubusercontent.com/kennycallado/rust-camel/main/schemas/dsl/route-schema.json - Local:
schemas/dsl/route-schema.json(regenerate withcargo xtask schema).
Using $schema
Add the $schema key to your JSON route files for editor autocomplete and SDK validation:
The parser silently ignores the key. See schemas/dsl/README.md for scope and versioning.
License
Apache-2.0