Architecture
The system-level architecture: the plane split, route lifecycle, shutdown and backpressure, and the crate dependency graph. The Core concepts section holds the mental model. This page builds on it and indexes the decisions that shaped it.
Data plane vs control plane
rust-camel separates message flow from lifecycle. The data plane is the hot
path. Every Exchange flows through a Tower Service<Exchange> pipeline. The
control plane is the cold path. It owns route lifecycle through a CQRS
RuntimeBus with optimistic versioning.
The Data plane vs control plane concept page covers the rationale, the performance and safety goals, and the Exchange trust boundary. ADR-0001 records the decision to adopt Tower as the data-plane foundation.
Route lifecycle
Route lifecycle follows a two-phase persist-then-execute pattern (ADR-0018).
The Runtime records intent before side effects, then confirms or compensates
after the side effect returns. For StartRoute, the sequence is:
- Record
RouteStartRequested, project Route asStarting - Start the Consumer and Pipeline
- Record
RouteStarted, projectStarted
If the side effect fails after intent was recorded, the Runtime records
RouteFailed, projects Failed, and publishes failure events. Compensation
applies to every non-atomic lifecycle flow (ADR-0018).
The lifecycle layer uses hexagonal architecture (ADR-0003). Domain and
application logic sit behind ports (RouteRepositoryPort,
ProjectionStorePort, RuntimeEventJournalPort, RuntimeExecutionPort).
Concrete adapters provide in-memory and redb implementations. ADR-0045 extends
this discipline crate-wide. Every behavioral area in camel-core is a vertical
slice with its own domain / application / ports / adapters layout.
Stateful pipeline steps (aggregators, resequencers, idempotent repositories)
implement the StepLifecycle trait (ADR-0022). The trait adds a drain hook
that outlives a single process() call. The drain lets background work
(timers, buckets, queues) complete before the step shuts down.
Shutdown and backpressure
A route stop signals in-flight pipelines through a tokio::task_local! cancel
token (ADR-0043). The step loop checks the token before each step and returns
Failed(ConsumerStopping) on cancel. This gives cooperative cancellation
without interrupting a step mid-.await. The
Data plane vs control plane page covers the token
mechanics and the graceful-drain ordering.
Route-admission backpressure (ADR-0044) caps concurrent in-flight Exchanges. The Concurrent consumer model acquires a semaphore permit before it dequeues work. When permits run out, the consumer blocks on the permit instead of buffering more work inside the pipeline task. This bounds memory under load.
Crate dependency overview
The crate dependency graph follows a layered structure. Contract crates sit at the bottom with zero or minimal internal dependencies. Runtime and processing crates depend on contracts. Components, languages, and services depend on the runtime and contracts. Platforms depend on services.
Platforms
|
Services
|
Components ----> Runtime ----> Processors
| | |
| v v
+---------> Contracts <--------+
^
|
Languages
Contracts (camel-api, camel-component-api, camel-language-api,
camel-wit, camel-config, camel-endpoint, camel-bean, camel-test,
camel-bench) define the types and traits that every other crate depends on.
They have no runtime dependency.
Runtime (camel-core, camel-cli, camel-health) owns the execution
engine, route lifecycle, hot reload, and registries. camel-core depends on
contract crates and drives every other family.
Processors (camel-processor, camel-builder) implement EIP patterns as
Tower middleware. They depend on camel-api for Exchange and Processor.
DSL (camel-dsl) parses YAML and JSON route definitions into
RouteDefinition. It depends on camel-api and camel-builder.
Components connect routes to external systems. Each component crate depends
on camel-component-api and camel-core. See the
component catalog for the full list.
Languages evaluate expressions and predicates. Each language crate depends
on camel-language-api. See the language catalog for
the full list.
Services provide cross-cutting infrastructure: auth, observability, and
function invocation. They register into CamelContext through the
service contracts. See the
service catalog for the full list.
Platforms expose deployment-aware behaviour. See the platform catalog for the full list.
Data formats convert between wire representations and structured body types. See the data format catalog for the full list.
For the complete bounded-context map and domain vocabulary, see CONTEXT-MAP.md.
ADR index
Architecture-shaping choices live as ADRs under
../adr/. The index below organizes them by topic. Each
entry links to the ADR file and gives a one-sentence summary.
See also Important Findings Summary for resolved P0 findings.
Architecture and Design
Core patterns, lifecycle, pipeline, and route authoring.
| ADR | Title | Summary |
|---|---|---|
| 0001 | Tower data plane, custom-trait control plane | Separates Exchange processing (Tower Service<Exchange>) from component lifecycle (custom traits). |
| 0002 | CQRS RuntimeBus for route lifecycle | Route lifecycle mutations go through RuntimeCommandBus with projections and optional event journal. |
| 0003 | Hexagonal lifecycle core | Lifecycle layer uses ports and adapters for persistence and testability. |
| 0004 | Hot reload via atomic pipeline swap | Pipeline swap uses ArcSwap so in-flight Exchanges complete against the snapshot they entered. |
| 0005 | Function out-of-process staged reload | function: steps run in isolated containers with prepare/finalize/discard registration. |
| 0006 | Script synchronous, async to function | JavaScript evaluation is synchronous; async paths delegate to function:. |
| 0007 | Route-supervised consumer failure | Consumer task failure is route-supervised with optional restart policy and backoff. |
| 0008 | Route templates via JSON tree substitution | Template placeholders expand via JSON tree walk before DSL deserialization. |
| 0009 | HTTP co-hosting API and static routes | API routes and static mounts share one server per host/port with deterministic dispatch. |
| 0011 | CanonicalRouteSpec minimal contract | v1 is a stable minimal route contract, not a full RouteDefinition mirror. |
| 0015 | Endpoint-created PollingConsumer | Pull-based adapter created from an Endpoint for pollEnrich and WASM camel_poll. |
| 0016 | CanonicalRouteSpec v2 contract | v2 adds lifecycle metadata with strict rejection for unsupported fields. |
| 0017 | DSL YAML snake_case naming | DSL keys use snake_case to match Rust field names and schema output. |
| 0018 | Two-phase route lifecycle persistence | Lifecycle commands persist intent before side effects, compensate on failure. |
| 0022 | StepLifecycle trait and drain | Stateful pipeline steps get a separate drain hook for background work. |
| 0024 | PipelineOutcome replaces CamelError::Stopped | PipelineOutcome enum replaces CamelError::Stopped for pipeline control flow. |
| 0025 | Outcome-aware structural EIPs | Structural EIPs return PipelineOutcome directly instead of Tower Result. |
| 0026 | JSON canonical route authoring | JSON is the canonical full-DSL format for SDKs and generators; YAML is human convenience. |
| 0029 | Resequencer continuation boundary | Compiler splits step list at Resequence; post-steps compile into a continuation owned by the service. |
| 0030 | Exchange-aware DataFormat hooks | DataFormat gains default marshal_in_exchange / unmarshal_in_exchange hooks. |
| 0031 | WASM source world | Fourth WIT world source lets WASM guests act as Consumers with their own consumption loop. |
| 0041 | Component metadata capabilities schema | ComponentMetadata schema with OptionKind, UriOption, ComponentCapabilities, CapabilityQuery. |
| 0042 | Arc<[CompiledStep]> shared snapshot | Shared snapshot avoids per-Exchange Vec clone for compiled pipeline steps. |
| 0043 | Pipeline cancellation between steps | task_local! cancel token checked between steps for cooperative cancellation. |
| 0044 | Route-admission backpressure | Semaphore permit acquired before dequeue prevents unbounded in-flight work. |
| 0045 | camel-core architecture charter | Codifies Clean + DDD + CQRS + vertical slices + hexagonal discipline crate-wide. |
| 0046 | Apache Camel inspiration, not conformance | Apache Camel is design inspiration, not conformance authority. |
| 0047 | Template rendering engine | MiniJinja-based external template engine with compile-once caching and atomic hot reload. |
| 0053 | WIT interface versioning | camel:plugin uses one package-level WIT SemVer, independent from Rust crate versions. |
Security
Authentication, authorization, trust boundaries, and capability models.
| ADR | Title | Summary |
|---|---|---|
| 0010 | SecurityPolicy pre-pipeline authorization | Route-level authorization wraps the Pipeline before any step runs. |
| 0032 | Exchange-data trust boundary | Operator config is trusted; exchange data is untrusted and must not drive control-plane actions. |
| 0033 | Security defaults and fail-closed startup validation | Five-disposition security policy enforced by a single startup-validation phase. |
| 0034 | ControlBus capability authorization | ControlBus requires an authorizedRoutes allowlist and denies self-restart. |
| 0035 | Leader-epoch fencing token | Every master: delegate envelope carries a monotonic fencing token for split-brain safety. |
| 0036 | Bridge IPC mutual TLS | Bridge uses mutual TLS with ephemeral certificates; fail-closed guard rejects placeholder paths. |
| 0037 | Exec component fail-closed capability model | Allowlisted binaries, argument policy, no shell, bounded stdin. |
| 0050 | WASM sandbox capability posture | Per-world grants for Camel host functions and selective WASI registration. |
| 0051 | Credential redaction at diagnostic boundaries | Credential-bearing types use manual redacting Debug; Serialize must not expose credential bytes. |
| 0052 | Diagnostic endpoint exposure posture | Diagnostic endpoints follow the Prometheus scrape model; network isolation is the operator's duty. |
Error Handling
Disposition, drain, supervision, and repository patterns.
| ADR | Title | Summary |
|---|---|---|
| 0012 | Log-level convention by handler-contract boundaries | Emitters inside a handler contract log at warn! or below; outside emitters may log at error!. |
| 0019 | Error disposition in-pipeline recovery | RouteErrorHandler trait injected into the pipeline decides disposition after each step failure. |
| 0023 | Idempotent Repository trait | Key-only IdempotentRepository trait in camel-api for duplicate detection. |
| 0028 | Claim Check Repository trait | Payload-bearing ClaimCheckRepository trait distinct from key-only IdempotentRepository. |
Performance and Limits
DoS caps, cardinality limits, and resource bounds.
| ADR | Title | Summary |
|---|---|---|
| 0038 | Configurable DoS caps | Per-format config channel for operator-overridable data-format DoS caps. |
| 0039 | Configurable loop iteration cap | Per-step max_iterations escape hatch for loop iteration limits. |
| 0040 | Configurable materialize limits | Configurable materialize limits for XSLT, XJ, and WASM producers. |
Integration
WASM, functions, components, and cross-cutting contracts.
| ADR | Title | Summary |
|---|---|---|
| 0013 | NetworkRetryPolicy and migration | Centralized retry semantics and migration boundaries for adapter retries. |
| 0014 | WASM plugin config unification | Unified WASM plugin runtime configuration across all plugin types. |
| 0020 | LLM component provider adapter boundary | LLM component isolates SDK behind a project-owned LlmProvider trait. |
| 0021 | LLM retry with retry-after manual loop | LLM retry honors provider retry_after via manual loop, diverging from ADR-0013. |
| 0027 | MQTT component 3.1.1 per-endpoint | MQTT 3.1.1 via rumqttc with one connection per Consumer or Producer. |
| 0048 | Attestation provenance (retired) | Retired HMAC-SHA256 attestation decision kept for history. |
| 0049 | Workspace non-exhaustive policy | Public contract enums are #[non_exhaustive] by default before the 1.0 API freeze. |