This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 5a452ec7987a CAMEL-26330: design - camel-chaos, faults turned on in a 
running Camel application (#28539)
5a452ec7987a is described below

commit 5a452ec7987a7af500063f3734d183fdf429e0c9
Author: allthingssecurity <[email protected]>
AuthorDate: Tue Oct 27 17:35:35 2026 +0530

    CAMEL-26330: design - camel-chaos, faults turned on in a running Camel 
application (#28539)
    
    Design document design/chaos.adoc for camel-chaos and its entry in the 
AGENTS.md design index.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 AGENTS.md         |   1 +
 design/chaos.adoc | 468 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 469 insertions(+)

diff --git a/AGENTS.md b/AGENTS.md
index e78bcca7c4d2..5a85c2332565 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -315,6 +315,7 @@ jump straight to implementation after reading the issue 
description and the curr
    - **MDC / Logging** (MDC propagation, logging context): 
[`design/mdc.adoc`](design/mdc.adoc)
    - **Headers** (naming conventions, constants, upgrade policy): 
[`design/headers.adoc`](design/headers.adoc)
    - **Cross-cutting features** (onException, interceptors, onCompletion, 
route configurations and how they bind to routes): 
[`design/cross-cutting-binding.adoc`](design/cross-cutting-binding.adoc)
+   - **Chaos** (proposed; `camel-chaos`: faults turned on in a running 
application, the send and step hooks in camel-core, `camel.chaos.*` options, 
the report): [`design/chaos.adoc`](design/chaos.adoc)
    - **Java DSL parsing** (reading Java routes into the model without 
compiling them, `LwJavaParser`, its safety rules): 
[`design/java-dsl-parser.adoc`](design/java-dsl-parser.adoc)
 5. **Understand the broader context**: If the issue involves a module that 
replaced or deprecated
    another (e.g., `camel-opentelemetry2` replacing `camel-opentelemetry`), 
understand *why* the
diff --git a/design/chaos.adoc b/design/chaos.adoc
new file mode 100644
index 000000000000..51c3b2a894f7
--- /dev/null
+++ b/design/chaos.adoc
@@ -0,0 +1,468 @@
+---
+title: Chaos
+authors:
+  - "@allthingssecurity"
+reviewers: ["@davsclaus"]
+approvers: []
+creation-date: 2026-10-26
+last-updated: 2026-10-27
+status: proposed
+see-also:
+  - "CAMEL-26330"
+  - "CAMEL-26331"
+  - "CAMEL-26338"
+  - "CAMEL-25911"
+  - "CAMEL-25684"
+  - "CAMEL-25682"
+  - "CAMEL-26286"
+replaces: []
+superseded-by: []
+---
+
+== Summary
+
+`camel-chaos` lets a developer, or an AI tool, turn on faults in a running 
Camel application and see how its routes
+cope. A rule such as "fail 30% of the sends to `kafka:orders` for one minute" 
or "stall route `poller` for 20 s" is
+added while the application runs, for example in dev mode (`camel run --dev`). 
A report then says per route how many
+exchanges completed, failed, were handled by the error handler or were 
redelivered, whether the consumer keeps a
+failed message, and, for a rule with an expectation, how many messages were 
lost, with a verdict.
+
+With the `camel-chaos` JAR and profile `dev` (the default of `camel run`), or 
`camel.chaos.enabled=true`, its hooks
+are installed and stay idle until a rule is added. Without the JAR, as in 
production, nothing of it is there. Faults
+run in real time, and the same rules can run inside a `camel-test-dst` world 
in virtual time.
+
+Chaos needs two small changes in camel-core: a hook for every send to an 
endpoint (CAMEL-26331) and a hook for every
+step, inside the error handler of the step (CAMEL-26338). Both are general 
extension points, not chaos-specific: their
+API names no chaos type, so other features and tests can use them. The other 
levels use SPIs that exist today.
+
+== Motivation
+
+Routes are written for the happy path, and their error handling is rarely 
exercised before production. Questions such
+as "what happens to the messages in flight when the broker is slow for 30 s", 
"does this `onException` really match the
+error the HTTP endpoint returns" or "does the consumer resume after its route 
was suspended" are answered today by
+hand-written tests, one route and one fault at a time.
+
+A prototype (CAMEL-26330 links it), which hooks only `to`, reproduced 
CAMEL-25684 (camel-slack, a route suspend in the
+middle of a poll) and CAMEL-25682 (camel-resilience4j, a timeout while the 
task writes its result) on the code from
+before their fixes. The recent "remove a route" bugs (CAMEL-26286, 
CAMEL-26287, CAMEL-26288, CAMEL-26302 to
+CAMEL-26305: one route goes, and another route loses the cache, channel or 
session it shares) have a shape a chaos
+rule can produce on demand.
+
+AI tools that write and change routes need the same: a way to try a route 
against failures and read a short result.
+
+== Goals
+
+- Out of the box chaos for any Camel application in development: faults on 
sends, on the messages that enter a route,
+  on consumer polls, on processing steps and on the route lifecycle.
+- Switched on by the classpath with profile `dev` or 
`camel.chaos.enabled=true`, idle until a rule is added; rules
+  added, changed and removed live.
+- Configuration through `camel.chaos.*` options of camel-main, as for 
`camel.debug.*` and `camel.trace.*`.
+- A report per route with counts and a verdict, readable by a person and by a 
tool.
+- Control from the CLI (`camel cmd chaos-add` and the other `chaos-*` 
commands, `camel get chaos`) and the
+  camel-jbang MCP server; a dev console shows the rules and the report.
+- Real time by default; the same rules in virtual time inside a DST world.
+- A seed fixes the random draws of the rules. The same report again needs a 
DST world (see Time).
+
+== Non-goals
+
+- Production use. Chaos fails closed, and stays off with profile `prod` even 
when enabled.
+- Custom fault types written by users. The first version has a fixed set; a 
plugin SPI (a JAR with its own faults) can
+  come later.
+- Faults outside Camel: network partitions, killing processes, disk or clock 
faults of the host. Tools outside Camel
+  do that; this is about what Camel does with the failures it sees.
+- The video, the models (TLA+, Lean, Z3) and the demo scripts of the 
prototype. They stay on the prototype branch.
+
+== Proposal
+
+=== Switched on by the classpath
+
+`camel-chaos` registers a `ContextServicePlugin` 
(`META-INF/services/org.apache.camel.spi.ContextServicePlugin`).
+The `CamelContext` loads the plugins found on the classpath while it is built, 
before the routes are created.
+
+In camel-main the profile and the `camel.chaos.*` options are set after the 
plugins are loaded, so the plugin reads
+them when the context initializes (`LifecycleStrategy.onContextInitializing`), 
which is still before the routes are
+created. The profile comes from `ExtendedCamelContext.getProfile()`.
+
+The plugin installs the hooks below only with profile `dev`, the default of 
`camel run`, or with
+`camel.chaos.enabled=true` (a plain `Main`, tests, DST worlds), and never with 
`prod`. Otherwise it fails closed. That
+includes no profile, the usual case for a plain `Main`, Spring Boot and 
Quarkus: the profile is then null, not `prod`
+(`ProfileConfigurer.configureMain` returns early), and a `camel.chaos.rules` 
left in `application.properties` does
+nothing, unless `camel.chaos.enabled=true` is left there too. When it installs 
the hooks, it logs a WARN that chaos is
+active. Each hook asks the `ChaosManager` whether a rule matches; with no 
rules that is one volatile read, and the call
+goes on unchanged.
+
+=== Getting it
+
+- `camel run --chaos` adds the `camel:chaos` dependency, as `--observe` adds 
`camel:observability-services` in
+  `Run.java`; `--dep camel:chaos` works too.
+- A plain `Main` or a test adds the `camel-chaos` dependency and sets 
`camel.chaos.enabled=true`.
+- Spring Boot and Quarkus: the plugin works with any `CamelContext` and 
installs the hooks with Camel profile `dev`.
+  The options are camel-main only, so rules come from the CLI connector; a 
starter or an extension can bind
+  `camel.chaos.*` later.
+
+=== Options
+
+[cols="1,1,3"]
+|===
+|Option |Default |Description
+
+|`camel.chaos.enabled` |`false`, `true` with profile `dev` |Install the hooks: 
`true` for a plain `Main`, tests and
+DST worlds, ignored with profile `prod`. They do nothing until a rule is added.
+|`camel.chaos.seed` | |Seed of the random draws of the rules; without it a 
seed is picked and shown in the report.
+|`camel.chaos.rules` | |Rules to add at startup, in the text form of Rule 
syntax, separated by `;` (marked
+`insecure:dev`, see Risks).
+|`camel.chaos.maxDuration` |`10m` |Upper bound for every rule: a rule without 
`until` ends after it, and a
+`duration` of `stall` or `lifecycle` may not be longer (such a rule is 
rejected when it is added).
+|`camel.chaos.reportOnShutdown` |`true` |Log the report when the context stops.
+|===
+
+They are a `ChaosConfigurationProperties` group in camel-main, as 
`DebuggerConfigurationProperties` is for
+`camel.debug.*`, so the catalog and tooling know them.
+
+=== Rules
+
+A rule has a target, a fault, how often it fires, and when it ends:
+
+[source]
+----
+target:  route=<id or pattern> | endpoint=<uri pattern> | component=<name> | 
step=<processor id pattern>
+at:      send | poll | entry | step   (optional, see Target and hooks)
+fault:   error | delay | drop | corrupt | lifecycle (stall)   (parameters: see 
Rule syntax)
+rate:    a probability (0.3), or every Nth match
+until:   a number of matches, a duration, or removed by hand
+----
+
+Rules are added, changed, listed and removed live; a rule gets an id when it 
is added, which change and remove take.
+A rule ends by itself after its count or duration (at most 
`camel.chaos.maxDuration`), so a forgotten rule cannot keep
+breaking a dev instance. Rules stay when the routes are reloaded in dev mode.
+
+==== Rule syntax
+
+A rule is one line: the target, the fault and its parameters as `name=value`, 
then `at`, `rate` and `until`.
+`rate=0.3` is a probability and `rate=every:5` fires on every 5th match; 
without `rate` every match fires. `until=100`
+is a number of matches and `until=2m` a duration; without `until` the rule 
ends after `camel.chaos.maxDuration`. A
+value with spaces is quoted. Where a rule fires is in Target and hooks below.
+
+A rule can also carry an expectation, `expect=<route id>:<count>` with an 
optional `within=<time>` (default: the rule's
+`until` duration, else `camel.chaos.maxDuration`): the route must complete at 
least that many more exchanges after the
+rule is added and before that time is up. For example
+`endpoint=kafka:orders* error rate=0.3 until=1m expect=orders:100 within=2m`. 
See The report for how it is checked.
+
+[cols="1,2,3"]
+|===
+|Fault |Parameters |Effect
+
+|`error`
+|`exception=<class>`, `message=<text>`; for the HTTP components 
`status=<code>`, `header.<name>=<value>` and
+`body=<text>`
+|Throws that exception. With `status` it is the exception the HTTP component 
throws for that status; when the endpoint
+has `throwExceptionOnFailure=false`, chaos returns the response instead, as 
the real producer does: the status in the
+`CamelHttpResponseCode` header, the `header.*` values and the `body` (empty 
without it). All parameters are optional:
+without `exception` (and without `status`) the fault throws a 
`ChaosException`, a `RuntimeCamelException` of
+camel-chaos, with the `message` or a default one, on any endpoint. A rule 
whose exception class is not found, is not
+a `Throwable`, or has neither a constructor taking a `String` message nor a 
no-argument one is rejected when it is
+added, with an error that names the class.
+
+|`delay`
+|`duration=<time>`, such as `500ms` or `2s`
+|Waits that long before the send, the step or the poll.
+
+|`drop`
+|none
+|A send that reports success without calling the producer, or a skipped poll.
+
+|`corrupt`
+|`mode=headers\|charset\|truncate`
+|Changes the message: header values with random data, a body in an unusual 
charset (characters outside US-ASCII), or
+an `InputStream` body that ends too soon. `charset` applies to a body that is 
text or converts to text, and `truncate`
+to an `InputStream` or stream cache body; on another body the message is left 
as it is and the report counts the rule
+as skipped for that message, so a fault that did nothing is visible.
+
+|`lifecycle`
+|`action=suspend\|stop\|remove`, `duration=<time>`
+|Suspends, stops or removes the target route, then resumes it, starts it or 
adds its definition again after the
+duration. `stall duration=<time>` is short for `lifecycle action=suspend 
duration=<time>`. Both take only a `route=`
+target; a rule with another target is rejected when it is added.
+|===
+
+`stall` and `lifecycle` act once on the route when the rule is added; `rate` 
and `until` do not apply to them, and
+their `duration` is bounded by `camel.chaos.maxDuration`.
+
+==== Target and hooks
+
+Each target has these hooks, and each hook offers some of the faults:
+
+[cols="2,3,3"]
+|===
+|Target |Hooks (`at=`) |Faults
+
+|`endpoint=`, `component=`
+|The sends to a matching endpoint (`send`) and the polls of its consumer 
(`poll`).
+|`send`: `error`, `delay`, `drop`, `corrupt`. `poll`: `error`, `delay`, `drop`.
+
+|`route=`
+|The messages that enter the route (`entry`), and the route lifecycle.
+|`entry`: `corrupt`. Lifecycle: `stall`, `lifecycle`.
+
+|`step=`
+|The matching steps (`step`).
+|`error`, `delay`.
+|===
+
+A rule fires at every hook of its target that offers its fault; `at=` narrows 
it to one (`stall` and `lifecycle`
+take no `at`). A rule whose `at` is not a hook of its target, or whose fault 
no hook of its target (after `at`) offers,
+is rejected when it is added, with an error that names the hooks of the 
target. A rule that matches nothing, such as a
+poll rule for a consumer that does not poll on a schedule (Kafka, JMS), fires 
0 times, as the report shows. So:
+
+- `endpoint=file:inbox* drop` (or `error`) fires at both hooks: a poll of the 
`file:inbox` consumer is skipped, and a
+  send to `file:inbox` reports success without writing. `at=poll` or `at=send` 
keeps one. With `corrupt` the rule
+  fires at the sends only.
+- `route=orders error` and `route=orders delay` are rejected: the entry of a 
route offers only `corrupt`. `route=`
+  does not reach the sends and steps of the route; they are targeted with 
`endpoint=`, `component=` or `step=`. One
+  kind of target per hook keeps a rule, and its counts in the report, in one 
kind of place.
+- `component=kafka` matches the sends to `kafka:` endpoints and the polls of 
its consumers (none: they do not poll
+  on a schedule), not the entry of a route that starts from Kafka, which is 
`route=`.
+
+In `application.properties`:
+
+[source,properties]
+----
+camel.chaos.seed = 42
+camel.chaos.rules = \
+  endpoint=kafka:orders* error exception=java.net.ConnectException 
message="broker down" rate=0.3 until=1m;\
+  endpoint=https:inventory* error status=503 header.Retry-After=5 rate=every:4 
until=50;\
+  route=poller stall duration=20s
+----
+
+With the CLI, against the running integration `myapp` (see Control):
+
+[source,bash]
+----
+camel cmd chaos-add myapp --rule='endpoint=kafka:orders* delay duration=2s 
rate=0.2 until=5m'
+camel cmd chaos-add myapp --rule='endpoint=file:inbox* drop at=poll rate=0.5 
until=2m'
+camel cmd chaos-add myapp --rule='route=orders corrupt mode=truncate rate=0.1 
until=20'
+camel cmd chaos-add myapp --rule='route=orders lifecycle action=remove 
duration=10s'
+camel get chaos myapp --report
+----
+
+=== Hooks at all levels
+
+[cols="1,2,2"]
+|===
+|Level |Hook |Faults
+
+|Sends to any endpoint
+|The send hook of camel-core (CAMEL-26331): one helper that the six places 
that fire `ExchangeSendingEvent` share
+(`SendProcessor`, `DefaultProducerCache` twice, `EventNotifierCallback`, 
`MulticastProcessor`,
+`EventNotifierProducer`), with a hook before and after the producer.
+|`error` (the producer is not called), `delay`, `drop` (a send that reports 
success), `corrupt` (the message sent).
+
+|Messages that enter a route
+|A `RoutePolicy`, added to every route by a `RoutePolicyFactory`. Its 
`onExchangeBegin` sees every exchange that
+enters a route and can change the message.
+|`corrupt`: header values with random data, a body in an unusual charset, an 
`InputStream` that ends too soon. Not
+`drop`: the exchange already belongs to its consumer, and dropping it there 
would make the consumer commit a message
+the route never processed (a file consumer deletes the file), a loss caused by 
chaos rather than by the route. `drop`
+is at the sends and the polls instead.
+
+|Consumer polls
+|The `PollingConsumerPollStrategy` of a `ScheduledPollConsumer`, which can be 
changed at runtime with
+`setPollStrategy`. Its `begin`, `commit` and `rollback` run around every poll. 
Chaos wraps the consumer's own strategy
+while a rule is active and puts it back when the rule ends.
+|A failed poll (`error`), a slow poll (`delay`), a skipped poll (`drop`).
+
+|Every step
+|Inside the error handler of the step (CAMEL-26338).
+|`delay` or `error` at a step, raised where the error handler calls the step, 
as for a send. (The check that skips a
+processor disabled with `camel cmd disable-processor` runs before the error 
handler, so the hook is not there.)
+
+|Route lifecycle
+|The `RouteController` (suspend, resume, stop, start), and 
`CamelContext.removeRoute` with the route definition added
+again.
+|`stall` (suspend, then resume); `lifecycle` (suspend, stop or remove the 
route, then bring it back).
+|===
+
+The send hook covers every send whatever started it: `to`, `toD`, `wireTap`, 
`enrich`, `recipientList`, `multicast`
+and a `ProducerTemplate`. The components stay the real ones (a 
`KafkaComponent` stays a `KafkaComponent`), and the
+routes get no extra processors. For a send or a step of a route the fault is 
raised inside the error handler, so it is
+a real failure for `onException`, redelivery and the dead letter channel.
+
+A fault has a real type where it matters, so that the `onException` of the 
route is what gets tested: for the HTTP
+components, the exception the component throws for a status code 
(`HttpOperationFailedException` of camel-http-base,
+`NettyHttpOperationFailedException` for camel-netty-http) with the status code 
and headers given in the rule (or the
+response, with `throwExceptionOnFailure=false`); for any other endpoint, the 
exception class and message given in the
+rule, or a `ChaosException` when the rule gives none.
+
+=== Time
+
+The `delay` fault sleeps through `TimeSource` (camel-util, CAMEL-25911), and 
the report reads its times through it.
+The timed actions (the resume or restart after `stall` or `lifecycle`, the end 
of a rule) are tasks on a scheduled
+executor of the `ExecutorServiceManager`, not threads of their own or a 
`java.util.Timer`. By default that is the
+system clock, so a person sees a 20 s stall last 20 s.
+
+The seed fixes the sequence of random draws, not which exchange gets which 
draw. In real time with concurrent
+consumers the threads decide that, so two runs with the same seed can give 
different reports.
+
+In a `camel-test-dst` world the harness runs those executors and installs its 
virtual clock in `TimeSource`: a 10 minute
+stall takes milliseconds, and the DST seed decides the order of the tasks. The 
world sets `camel.chaos.seed` to that
+seed (`Dst.seed()`, from `@DstTest`, `@DstSeed` or `-Ddst.seed`), or to a 
value of its own; with both fixed, a run
+gives the same report again, times included. `camel-chaos` does not depend on 
`camel-test-dst`. Event timestamps stay
+on `System` in a DST run (`test-dst.adoc`), so the report does not use them. A 
`stall` or `lifecycle` rule meets the
+one-stack model of `test-dst.adoc`: a suspend or stop cannot see an exchange 
that is blocked in a synchronous call
+below it on the stack finish, so such a world calls its downstream with 
`DstCamel.asyncStep`. Chaos does not need DST.
+
+The Javadoc of `TimeSource` says it is internal and not an API for components. 
`camel-chaos` may use it: it reads the
+clock and sleeps through it, and never installs a provider (only a test 
harness such as `camel-test-dst` does that).
+The Javadoc is updated to say so with the first chaos pull request.
+
+=== The report
+
+The plugin counts per route with an event notifier, as the prototype does: 
completed, failed, handled by the error
+handler, redelivered, and the faults that fired. Attempts are not tied to one 
message: each exchange is counted once,
+so a file that fails and completes on the next poll is one failed and one 
completed exchange. Tying them by a key (the
+file name, `JMSMessageID`, the Kafka offset) is not in the first version.
+
+A failed exchange shows as "failed, kept by the consumer" when Camel knows 
that the consumer keeps the message and
+reads it again, and as "failed" otherwise. (An exchange the error handler 
handled is not failed; its consumer commits
+it.) The plugin reads the consumer's endpoint options through its generated 
configurer (`PropertyConfigurerGetter`),
+so options set on the component count too, without a dependency on the 
components:
+
+[cols="1,3,2"]
+|===
+|Consumer |Kept |Not kept
+
+|`file`, `ftp`, `sftp`, `ftps`
+|Without `moveFailed`: the rollback leaves the file (with `noop=true` it 
removes it from the idempotent repository),
+and the next poll reads it again.
+|With `moveFailed`.
+
+|`jms`, `activemq`, `amqp`
+|`transacted=true`, or `acknowledgementModeName=CLIENT_ACKNOWLEDGE` with 
`asyncConsumer=false` (the default): the
+listener throws, and the session rolls back or recovers.
+|Otherwise.
+
+|`kafka`
+|`breakOnFirstError=true`: the failed offset is not committed, and the 
consumer reads the record again.
+|`breakOnFirstError=false` (the default).
+
+|Other consumers
+|
+|Always.
+|===
+
+Without an expectation the report never says "lost", and the verdict is PASS: 
chaos makes exchanges fail on purpose.
+A rule can carry an expectation (`expect=orders:100 within=2m`: route `orders` 
completes 100 more messages within
+2m), which also finds messages lost before they became an exchange. Only an 
unmet expectation turns PASS into FAIL,
+with the reason, and "lost" only comes from one (100 expected, 97 completed: 3 
lost). The report is plain text for the
+CLI and JSON for tools, and states the seed and the rules that were active.
+
+An expectation is decided only when its count is reached (met) or when 
`within` has elapsed without it (unmet, FAIL).
+Before that it is "pending", with the count so far and the time left, and the 
verdict stays PASS. It stays pending
+when the context stops first (the report of `reportOnShutdown`) or the rule is 
removed first: the messages may still
+arrive, so they are not called lost. A tool must treat a pending expectation 
as not yet decided, so a PASS with one
+pending is not final.
+
+=== Control
+
+- A `chaos` dev console lists the rules and shows the report, so it is 
`@DevConsole(readOnly = true)`. It does not add,
+  change or remove: the flag is metadata only, and `/q/dev/*` 
(`ManagementHttpServer`) takes GET and POST with the
+  options in the query string, without checking it. So over HTTP chaos only 
lists and reports.
+- A `chaos` action of the CLI connector (`LocalCliConnector`) adds, changes 
and removes rules through the
+  `ChaosManager`. The CLI has the flat verb-noun shape of every `camel cmd` 
today (`stop-route`, `stub`), with the
+  reads under `camel get` (as `camel get circuit-breaker`): `camel cmd 
chaos-add`, `chaos-change`, `chaos-remove`,
+  `chaos-clear`, and `camel get chaos` (the rules, or the report with 
`--report`), the same six operations as the MCP
+  tools. Each takes the integration name or pid first. `chaos-add` and 
`chaos-change` need it to match exactly one
+  integration, as `camel cmd sql` does; the others may omit it and then act on 
all, as `stop-route` does.
+- The camel-jbang MCP server gets a `chaos` runtime tool group next to `sql`, 
`tracing`, `resilience` and `http`,
+  offered only when the application has `camel-chaos`. The tools are in 
`RuntimeTools` (`@McpSecured`) and go through
+  the CLI connector, as the other runtime tools do; the shared `ToolRegistry` 
(also used by the TUI) has the same marks:
++
+[cols="2,1,1"]
+|===
+|Tool |`readOnlyHint` |`destructiveHint`
+
+|`camel_runtime_chaos_add`, `camel_runtime_chaos_change` |`false` |`true`
+|`camel_runtime_chaos_remove`, `camel_runtime_chaos_clear` |`false` |`false`
+|`camel_runtime_chaos_list`, `camel_runtime_chaos_report` |`true` |`false`
+|===
++
+With `camel.mcp.security.enabled=true` and 
`camel.mcp.security.access-level=read-write`, add and change are refused,
+as `camel_runtime_route_control`, `_sql`, `_send` and `_receive` are; 
`read-only` leaves list and report.
+- The TUI and `camel ask` ask the user before an AI adds or changes a fault, 
with the rule shown. Their AI tools ask
+  only for the calls in `ToolConfirms` (CAMEL-25676), not by the destructive 
flag, so the chaos PR adds these two
+  there and to "Developer tools" of the security model.
+- JMX is not needed for the first version.
+
+=== Order of work
+
+Each step is its own pull request after this document:
+
+. camel-core: the send hook (CAMEL-26331).
+. The module, the plugin, the options, the rule model, the report, and the 
faults on sends.
+. The dev console, the `chaos` action of the CLI connector, the `chaos-*` 
commands, `camel get chaos` and
+  `camel run --chaos`.
+. Faults on the messages that enter a route, and on consumer polls.
+. camel-core: the step hook in the channel (CAMEL-26338), then the faults at 
steps.
+. Route lifecycle faults.
+. The MCP tool group.
+. A DST world that runs chaos rules in virtual time, as an example and a 
regression test.
+
+== Alternatives considered
+
+- *Wrapping the component*, as `camel run --stub` swaps in the stub component 
where the component resolver of camel
+  run creates it. It sees every send, but it changes the type of the 
component: a `KafkaComponent` is then a wrapper
+  for `getComponent("kafka", KafkaComponent.class)`, the component options, 
the dev consoles and the health checks.
+- *`camel run --chaos` only, no module*, as `--stub` lives in camel run. A 
plain `Main`, a test and a DST world would
+  not get chaos, and neither would Spring Boot or Quarkus later. With the 
module, `--chaos` only adds a dependency.
+- *An event notifier on `ExchangeSendingEvent`.* It fires for every send but 
cannot stop one: an exception thrown by an
+  event notifier is logged and ignored, and the producer is called anyway.
+- *`interceptSendToEndpoint`.* It can stop a send, but it wraps the endpoints, 
and it needs an intercept definition in
+  the routes or a route configuration, which the user then sees in the route 
model.
+- *An `InterceptStrategy` for the steps.* It needs no camel-core change, but 
it wraps every processor of every route
+  in one more processor. The channel already runs a check per step for 
disabled processors.
+- *Faults from outside the JVM* (a proxy in front of the broker, killing 
containers). Useful, but it cannot fail one
+  route and not another, cannot produce a typed exception for `onException`, 
and needs infrastructure a developer
+  running `camel run --dev` does not have.
+- *Advice-with in tests.* It changes routes for one test; chaos is for a 
running application, and does not change the
+  routes.
+
+== Compatibility
+
+Nothing changes for applications without the JAR. The two camel-core hooks 
cost one check each when no hook is
+installed, like the event check that the send places do today. With the hooks 
installed and no rules, the send hook, a
+route policy per route and the step hook are idle: one volatile read per send, 
per exchange that enters a route and
+per step, a development-time cost only. The new options are in a new group.
+
+== Risks
+
+- The JAR left on the classpath of a production build. Mitigations: the hooks 
fail closed (only with `dev` or
+  `camel.chaos.enabled=true`, never with `prod`), a WARN at startup, and rules 
that expire. Rules come only from
+  `camel.chaos.rules` and the CLI connector, which the MCP tools use; over 
HTTP the dev console only lists and reports.
+- `camel.chaos.rules` is an `insecure:dev` option: `@Metadata(security = 
"insecure:dev")` on its field in
+  `ChaosConfigurationProperties`, as on `devConsoleEnabled` in 
`DefaultConfigurationProperties`. Profile `prod` sets
+  `camel.security.policy=fail`, so it refuses to start with rules configured; 
profile `dev` allows it, and with no
+  profile the default policy `warn` logs a WARN (and the hooks are not 
installed). In `SecurityUtils`,
+  `SECURITY_OPTIONS` is keyed by the bare option name and the schematron 
component has an endpoint option `rules`, so
+  the chaos module pull request limits the new entry to keys under 
`camel.chaos.`; it also lets any non-empty value of
+  that option count, where `isInsecureValue` compares with one insecure value.
+- A verdict that is wrong. The report states what it counted and from where, 
so a person can check it.
+
+== Open questions
+
+Each has a proposed answer; the maintainers decide.
+
+- Module location. Proposed: `components/camel-chaos`, next to `camel-debug`.
+- Should chaos stay off when no profile is set? Decided in review: yes (see 
"Switched on by the classpath"). No
+  profile is the usual case in production too, and `camel run` already 
defaults to `dev`.
+- The rule format. Proposed: the one-line text form of Rule syntax, the same 
in `camel.chaos.rules`,
+  `camel cmd chaos-add` and the MCP tools. A YAML form can come later if 
wanted.
+
+== Related tickets
+
+- CAMEL-26330: this feature.
+- CAMEL-26331: the send hook in camel-core.
+- CAMEL-26338: the step hook in camel-core.
+- CAMEL-25911: `TimeSource` in camel-util.
+- CAMEL-25684, CAMEL-25682: bugs the prototype reproduced on the code from 
before their fixes.
+- CAMEL-26286, CAMEL-26287, CAMEL-26288, CAMEL-26302 to CAMEL-26305: the 
"remove a route" bugs that a lifecycle rule
+  produces.

Reply via email to