diff --git a/website/docs/concepts/configuration.md b/website/docs/concepts/configuration.md
index 1268bd604..1f8fa5a06 100644
--- a/website/docs/concepts/configuration.md
+++ b/website/docs/concepts/configuration.md
@@ -137,6 +137,7 @@ Or split them into separate files for clarity:
forage-datasource-factory.properties # JDBC configuration
forage-connectionfactory.properties # JMS configuration
forage-agent-factory.properties # AI agent configuration
+forage-security-tls.properties # TLS/SSL configuration
```
## Required vs Optional Properties
@@ -180,7 +181,18 @@ Each name registers a separate bean. Use them in routes by name:
query: select * from orders
dataSource: "#ordersDb"
- to:
- uri: jms:queue:events
- parameters:
- connectionFactory: "#primaryBroker"
+ uri: primaryBroker:queue:events
+```
+
+The same pattern works for TLS profiles:
+
+```properties
+# Internal mTLS
+forage.internal.tls.keystore.path=/certs/internal.p12
+forage.internal.tls.keystore.password=changeit
+forage.internal.tls.keystore.type=PKCS12
+
+# External trust-only
+forage.external.tls.truststore.path=/certs/external-ca.jks
+forage.external.tls.truststore.password=trustme
```
diff --git a/website/docs/examples/ai/multi-agent.md b/website/docs/examples/ai/multi-agent.md
index 8ee2524c0..ad5ffc8a2 100644
--- a/website/docs/examples/ai/multi-agent.md
+++ b/website/docs/examples/ai/multi-agent.md
@@ -133,3 +133,4 @@ Both routes fire once. The Gemini agent receives "give the details of user 123",
- **Mix providers freely**: combine cloud APIs (Gemini, OpenAI, Anthropic) with local models (Ollama) by changing `model.kind`.
- **Tags control tool access**: only agents with matching tags can invoke a given tool. Agents without tags operate without tools.
- **Configuration isolation**: each agent has its own model, memory, and API credentials. Changing one agent does not affect the other.
+- **Deterministic provider selection**: when multiple providers of the same type are on the classpath, Forage selects deterministically by `@ForageBean` value rather than classpath order.
diff --git a/website/docs/examples/ai/rag.md b/website/docs/examples/ai/rag.md
index 0ddfed23e..858a7148a 100644
--- a/website/docs/examples/ai/rag.md
+++ b/website/docs/examples/ai/rag.md
@@ -138,3 +138,4 @@ Without access to the knowledge base, the agent cannot confirm the refund amount
- **Automatic document processing**: Forage handles chunking, embedding, and loading the knowledge base file at startup.
- **Tunable retrieval**: `max.results` and `min.score` control how much context the agent receives, balancing relevance against noise.
- **Same agent pattern**: the route looks identical to a non-RAG agent. The RAG behavior is entirely driven by configuration.
+- **Fail-fast on misconfiguration**: if RAG properties are present but the pipeline cannot be assembled (e.g., missing embedding provider dependency), Forage fails at startup with an actionable error instead of silently running without RAG.
diff --git a/website/docs/guides/migration.md b/website/docs/guides/migration.md
index b9c178619..968fba2ec 100644
--- a/website/docs/guides/migration.md
+++ b/website/docs/guides/migration.md
@@ -1,8 +1,78 @@
# Migration Guide
-This guide helps you migrate from framework-specific configuration to Forage's unified approach.
+This guide covers both version-to-version upgrade notes and migrating from framework-specific configuration to Forage.
-## Why Migrate to Forage?
+---
+
+## Upgrading to 1.5.0
+
+### Breaking Changes
+
+#### Guardrails require explicit opt-in
+
+Guardrails no longer activate by classpath presence alone. You must explicitly list the guardrails to enable:
+
+```properties
+# AgentCreator (forage-agent)
+forage.myAgent.agent.guardrails.input=pii-detector,keyword-filter
+forage.myAgent.agent.guardrails.output=pii-redactor
+
+# MultiAgentFactory (forage-agent-factories)
+forage.guardrails.input=pii-detector
+forage.guardrails.output=pii-redactor
+```
+
+Values are comma-separated `@ForageBean` names. The old `forage.guardrails.input.classes` property (FQCN-based) is replaced.
+
+Selected guardrails that fail to create now **throw at startup** instead of being silently skipped (fail closed).
+
+#### Default model names updated
+
+If you relied on the default model name without setting `model.name` explicitly, the defaults have changed:
+
+| Provider | Old Default | New Default |
+|---|---|---|
+| OpenAI | `gpt-3.5-turbo` | `gpt-4o-mini` |
+| Anthropic | `claude-3-haiku-20240307` | `claude-haiku-4-5-20251001` |
+| Ollama (chat) | `llama3` | `llama3.2` |
+| Ollama (embeddings) | `llama3` | `nomic-embed-text` |
+
+#### Removed configuration entries
+
+The following properties have been removed. Setting them now produces an `UNKNOWN_PROPERTY` warning in strict mode:
+
+**Hugging Face** — 6 unsupported entries removed: `top.k`, `top.p`, `do.sample`, `repetition.penalty`, `max.retries`, `log.requests.and.responses`
+
+**WatsonX AI** — 5 unsupported entries removed: `top.k`, `min.new.tokens`, `max.retries`, `repetition.penalty`, `timeout`
+
+**Agent config** — 5 dead memory entries removed: `agent.memory.redis.host`, `agent.memory.redis.port`, `agent.memory.infinispan.host`, `agent.memory.infinispan.port`, `agent.memory.infinispan.cache.name` (standalone memory modules have their own config)
+
+### Behavioral Changes
+
+#### Misconfiguration now fails fast
+
+Previously, several misconfiguration scenarios were silently ignored. They now fail at startup with actionable error messages:
+
+- **Unknown JMS kind** (e.g., `forage.jms.kind=typo`): previously fell back to Artemis silently; now throws `IllegalArgumentException` listing valid kinds.
+- **Unknown JDBC db.kind**: previously resulted in a `NullPointerException`; now throws `IllegalStateException` listing available providers.
+- **RAG assembly failure**: previously logged at TRACE and returned null (agent ran without RAG); now throws at startup when embedding config is present but the pipeline can't be assembled.
+- **Agent creation failure**: previously logged at WARN and swallowed; now rethrows at startup.
+
+#### Azure OpenAI and Chroma logging defaults
+
+Request/response logging for Azure OpenAI and Chroma vector database was **enabled by default** due to an inverted null-check. This has been corrected — logging is now **off by default**, matching all other providers. If you relied on the implicit logging, set `log.requests.and.responses=true` explicitly.
+
+#### JMS per-broker transaction scoping
+
+Each named JMS broker prefix now gets its own `JmsComponent` registered under the prefix name, with self-contained transaction semantics. See the [JMS module docs](../modules/jms.md#per-broker-components) for details.
+
+---
+
+## Migrating to Forage
+
+This section helps you migrate from framework-specific configuration to Forage's unified approach.
+
+### Why Migrate to Forage?
Forage solves a fundamental problem: **configuration fragmentation across runtimes**. Spring Boot and Quarkus use completely different property naming conventions for the same functionality, making it difficult to:
@@ -14,7 +84,7 @@ Forage solves a fundamental problem: **configuration fragmentation across runtim
---
-## The Configuration Fragmentation Problem
+### The Configuration Fragmentation Problem
### JDBC Example: Three Different Ways
@@ -64,7 +134,7 @@ The same PostgreSQL datasource requires different configuration in each runtime:
---
-## JDBC Migration
+### JDBC Migration
### From Spring Boot
@@ -369,7 +439,7 @@ forage.analyticsDb.jdbc.pool.max.size=15
---
-## JMS Migration
+### JMS Migration
### From Spring Boot
@@ -593,7 +663,7 @@ forage.backupBroker.jms.pool.max.connections=10
---
-## AI Agent Migration
+### AI Agent Migration
### From Manual LangChain4j Setup
@@ -730,7 +800,7 @@ forage.ollamaAgent.agent.features=memoryless
---
-## Configuration Comparison Tables
+### Configuration Comparison Tables
### JDBC Configuration
@@ -756,7 +826,7 @@ forage.ollamaAgent.agent.features=memoryless
---
-## Real-World Example: Multi-Database Application
+### Real-World Example: Multi-Database Application
### Scenario
@@ -881,7 +951,7 @@ export ORDERS_PASS=prod_pass
---
-## Getting Help
+### Getting Help
If you encounter issues during migration:
diff --git a/website/docs/guides/troubleshooting.md b/website/docs/guides/troubleshooting.md
index 602aa93f7..4d3f5f771 100644
--- a/website/docs/guides/troubleshooting.md
+++ b/website/docs/guides/troubleshooting.md
@@ -76,6 +76,77 @@ camel run * --strict
The validator uses Levenshtein distance to suggest corrections. See the [Property Validation](../guides/camel-jbang.md#property-validation) guide for details.
+### Unknown Provider Kind
+
+**Error:**
+```
+IllegalArgumentException: Unknown JMS kind 'activemq'. Valid options: [artemis, ibm-mq]
+```
+
+or:
+
+```
+IllegalStateException: No DataSourceProvider found for kind 'postgres'. Available providers: [postgresql, mysql, ...]
+```
+
+**Causes:**
+
+- Typo in `jms.kind` or `db.kind` value
+- Missing provider dependency on the classpath
+
+**Solutions:**
+
+1. **Check spelling** — use the exact kind name from the error message's valid options list.
+2. **Add the provider dependency:**
+ ```xml
+
+ io.kaoto.forage
+ forage-jdbc-postgresql
+ {{ forage_version }}
+
+ ```
+
+### RAG Assembly Failure
+
+**Error:**
+```
+IllegalStateException: Failed to assemble RAG pipeline: no EmbeddingModelProvider found on the classpath
+```
+
+**Causes:**
+
+- Embedding model properties are configured but the embedding provider jar is missing
+- Vector store cannot be initialized (e.g., connection refused)
+
+**Solutions:**
+
+1. **Add the embedding provider dependency:**
+ ```xml
+
+ io.kaoto.forage
+ forage-model-embeddings-ollama
+ {{ forage_version }}
+
+ ```
+2. Verify the embedding model service is running and reachable.
+
+### Guardrail Creation Failure
+
+**Error:**
+```
+RuntimeForageException: Failed to create guardrail 'pii-detector': ...
+```
+
+**Cause:**
+
+A guardrail was explicitly selected via `forage.agent.guardrails.input` but could not be created (missing dependency, misconfiguration).
+
+**Solution:**
+
+1. Verify the guardrail `@ForageBean` value is correct.
+2. Ensure the guardrail dependency is on the classpath.
+3. Check the full stack trace for the root cause (missing config, connection failure, etc.).
+
### Bean Reference Errors
**Error:**
diff --git a/website/docs/modules/ai/agents.md b/website/docs/modules/ai/agents.md
index c068048d0..e36b3b6fe 100644
--- a/website/docs/modules/ai/agents.md
+++ b/website/docs/modules/ai/agents.md
@@ -32,10 +32,47 @@ forage.myAgent.agent.memory.max.messages=20
{{ forage_beans_table("Agent", "Memory") }}
-## Available Input Guardrails
+## Guardrails
+
+Guardrails validate agent inputs and outputs (e.g., PII detection, keyword filtering). They must be **explicitly enabled** — adding a guardrail jar to the classpath is not enough.
+
+### Configuration
+
+List guardrails by their `@ForageBean` value (comma-separated):
+
+```properties
+forage.myAgent.agent.guardrails.input=pii-detector,keyword-filter
+forage.myAgent.agent.guardrails.output=pii-redactor
+```
+
+For `MultiAgentFactory` (shared across agents):
+
+```properties
+forage.guardrails.input=pii-detector
+forage.guardrails.output=pii-redactor
+```
+
+!!! warning "Fail-closed semantics"
+ If a selected guardrail cannot be created (missing dependency, misconfiguration), the application
+ fails at startup with a descriptive error. Guardrails are security controls — they are never
+ silently skipped.
+
+### Available Input Guardrails
{{ forage_beans_table("Agent", "Input Guardrail") }}
-## Available Output Guardrails
+### Available Output Guardrails
{{ forage_beans_table("Agent", "Output Guardrail") }}
+
+## Multimodal Content
+
+Agents support multimodal inputs (images, PDFs) in both memory and memoryless modes. When memory is enabled, multimodal content is preserved alongside text in the conversation history.
+
+## Memory Isolation
+
+Each agent bean gets its own isolated memory store. Two agents using the same `memory.kind` do not share conversation history — even if they use the same `memory.kind` and `memory.max.messages` values.
+
+## Provider Selection
+
+When multiple providers of the same type are on the classpath, selection is deterministic: providers are matched by their `@ForageBean` value against the `model.kind` (or equivalent) property. If no match is found, Forage fails fast with an error listing available providers.
diff --git a/website/docs/modules/ai/chat-memory.md b/website/docs/modules/ai/chat-memory.md
index 0a891fb59..638cdfb0d 100644
--- a/website/docs/modules/ai/chat-memory.md
+++ b/website/docs/modules/ai/chat-memory.md
@@ -6,6 +6,10 @@ Memory providers store conversation history for AI agents. Select one with the `
{{ forage_beans_table("Agent", "Memory") }}
+## Isolation
+
+Each agent gets its own isolated memory store instance. Two agents configured with the same `memory.kind` do not share conversation history. Named/prefixed agent configurations (e.g., `forage.foo.agent.*` vs `forage.bar.agent.*`) create fully independent memory stores.
+
## Message Window
In-memory sliding window that retains the last N messages. Simple and fast — no external infrastructure needed.
@@ -20,7 +24,7 @@ forage.myAgent.agent.memory.max.messages=20
## Redis
-Persistent conversation storage using Redis. Conversations survive application restarts.
+Persistent conversation storage using Redis. Conversations survive application restarts. Connections are initialized lazily on first use and cleaned up on shutdown.
```properties
forage.myAgent.agent.features=memory
@@ -33,7 +37,7 @@ forage.myAgent.agent.memory.redis.port=6379
## Infinispan
-Distributed conversation storage using Infinispan. Suitable for clustered deployments.
+Distributed conversation storage using Infinispan. Suitable for clustered deployments. Connections are initialized lazily on first use and cleaned up on shutdown.
```properties
forage.myAgent.agent.features=memory
diff --git a/website/docs/modules/ai/vector-databases.md b/website/docs/modules/ai/vector-databases.md
index 26206bbc7..b9e331368 100644
--- a/website/docs/modules/ai/vector-databases.md
+++ b/website/docs/modules/ai/vector-databases.md
@@ -2,6 +2,42 @@
Vector databases store embeddings for similarity search, used in RAG (Retrieval-Augmented Generation) pipelines.
+## Available Providers
+
+{{ forage_beans_table("Agent", "Embedding Store") }}
+
## RAG Configuration
{{ forage_bean_properties("Agent", "RAG", "defaultRag") }}
+
+## Provider Notes
+
+### In-Memory Store
+
+Zero-dependency store for development. Loads documents from a file, chunks them, and embeds them at startup.
+
+```properties
+forage.myAgent.agent.in.memory.store.file.source=knowledge-base.txt
+forage.myAgent.agent.in.memory.store.max.size=300
+forage.myAgent.agent.in.memory.store.overlap.size=100
+```
+
+### Pinecone
+
+Supports automatic index creation when `create.index=true` is set alongside `dimension`, `cloud`, and `region`.
+
+### Milvus
+
+The `database.name` property is now wired to the builder. Port defaults to `19530` if not set.
+
+### Weaviate
+
+Port defaults to `8080` (REST) and `50051` (gRPC). gRPC is disabled by default (`use.grpc.for.inserts=false`).
+
+### Qdrant
+
+Port defaults to `6334` if not set.
+
+### PgVector
+
+Port defaults to `5432` if not set.
diff --git a/website/docs/modules/index.md b/website/docs/modules/index.md
index 66ffb109f..b898d59c5 100644
--- a/website/docs/modules/index.md
+++ b/website/docs/modules/index.md
@@ -19,3 +19,7 @@ Forage provides ready-to-use bean factories for a wide range of Apache Camel com
## Integration
- [CXF](cxf.md) — SOAP web service endpoints
+
+## Security
+
+- [TLS](security/tls.md) — SSLContextParameters from properties (keystore, truststore, cipher suites)
diff --git a/website/docs/modules/jdbc.md b/website/docs/modules/jdbc.md
index 0415eb835..c30f39a67 100644
--- a/website/docs/modules/jdbc.md
+++ b/website/docs/modules/jdbc.md
@@ -27,6 +27,10 @@ forage.myDb.jdbc.password=secret
{{ forage_properties("DataSource") }}
+!!! info "Unknown db.kind"
+ Setting an unrecognized `db.kind` value now fails at startup with an error listing the
+ available providers, instead of producing a `NullPointerException` later.
+
## Multiple Datasources
Use different names to configure multiple databases:
diff --git a/website/docs/modules/jms.md b/website/docs/modules/jms.md
index 12e8e28e2..96ddc6262 100644
--- a/website/docs/modules/jms.md
+++ b/website/docs/modules/jms.md
@@ -35,6 +35,24 @@ forage.primaryBroker.jms.url=tcp://broker1:61616
forage.backupBroker.jms.kind=artemis
forage.backupBroker.jms.url=tcp://broker2:61617
```
+### Per-Broker Components
+
+Each named broker prefix registers its own `JmsComponent` in the Camel context, so routes can
+reference the prefix name directly as the component:
+
+```yaml
+# Uses the primaryBroker component (and its connection factory)
+- from:
+ uri: primaryBroker:queue:orders
+
+# Uses the backupBroker component
+- to:
+ uri: backupBroker:queue:audit
+```
+
+This replaces the older pattern of using `jms:` with a `connectionFactory` parameter.
+The default (unprefixed) configuration continues to use the `jms` component for backwards
+compatibility.
## XA Transactions
@@ -48,6 +66,40 @@ Setting `forage.jms.transaction.enabled=true` switches the module to XA mode:
- The Camel JMS component is configured with a JTA transaction manager, so consumers receive
each message inside a JTA transaction and a rollback returns the message to the broker.
+### Mixed XA and Non-XA Brokers
+
+Transaction wiring is scoped per broker. In a mixed setup — one XA broker for transactional
+work, one plain broker for fire-and-forget — each broker's semantics are self-contained:
+
+```properties
+# XA broker — full two-phase commit
+forage.xaBroker.jms.kind=ibm-mq
+forage.xaBroker.jms.broker.url=localhost(1414)
+forage.xaBroker.jms.transaction.enabled=true
+
+# Plain broker — no transaction manager
+forage.plainBroker.jms.kind=artemis
+forage.plainBroker.jms.broker.url=tcp://localhost:61616
+```
+
+Routes reference each broker by prefix:
+
+```yaml
+# Transactional consumption from the XA broker
+- from:
+ uri: xaBroker:queue:orders
+ steps:
+ - transacted: {}
+ - to: sql:insert into orders values(:#id, :#name)?dataSource=#myDb
+
+# Non-transactional send to the plain broker
+- to:
+ uri: plainBroker:queue:audit
+```
+
+The `xaBroker` component gets a `JtaTransactionManager`; the `plainBroker` component does not.
+This avoids the overhead and confusion of wrapping non-XA sessions in JTA transactions.
+
!!! warning "Endpoint contract"
Leave `transacted` at its default (`false`) on `jms:` endpoints. The JTA transaction manager
wired into the component drives the transaction; enabling the endpoint's *local* JMS
diff --git a/website/docs/modules/security/tls.md b/website/docs/modules/security/tls.md
new file mode 100644
index 000000000..53c2052c6
--- /dev/null
+++ b/website/docs/modules/security/tls.md
@@ -0,0 +1,85 @@
+# TLS
+
+Forage creates `SSLContextParameters` beans from properties, eliminating the need to hand-write keystore/truststore wiring in Java. Any Camel component that accepts `sslContextParameters` (HTTP, Netty, FTPS, Kafka, CXF, etc.) can reference the bean by name.
+
+## Quick Start
+
+```properties
+forage.tls.keystore.path=server.p12
+forage.tls.keystore.password=changeit
+forage.tls.keystore.type=PKCS12
+forage.tls.truststore.path=truststore.jks
+forage.tls.truststore.password=trustme
+forage.tls.secure.socket.protocol=TLSv1.3
+```
+
+This registers an `SSLContextParameters` bean named `sslContextParameters` in the Camel registry. Use it in routes:
+
+```yaml
+- to:
+ uri: https://api.example.com/orders
+ parameters:
+ sslContextParameters: "#sslContextParameters"
+```
+
+## Properties
+
+| Property | Description | Default |
+|---|---|---|
+| `forage.tls.keystore.path` | Path to the keystore file (filesystem or `classpath:` URI) | — |
+| `forage.tls.keystore.password` | Keystore password | — |
+| `forage.tls.keystore.type` | Keystore type (`JKS`, `PKCS12`, etc.) | `JKS` |
+| `forage.tls.truststore.path` | Path to the truststore file | — |
+| `forage.tls.truststore.password` | Truststore password | — |
+| `forage.tls.truststore.type` | Truststore type | `JKS` |
+| `forage.tls.client.authentication` | Client auth mode: `NONE`, `WANT`, or `REQUIRE` | `NONE` |
+| `forage.tls.cipher.suites` | Comma-separated list of cipher suites | — |
+| `forage.tls.secure.socket.protocol` | TLS protocol version | `TLSv1.3` |
+
+All properties are optional. At least one of `keystore.path` or `truststore.path` must be set for a bean to be created.
+
+## Named Profiles
+
+Use prefixed names to create multiple TLS configurations:
+
+```properties
+# Internal services — mutual TLS
+forage.internal.tls.keystore.path=/certs/internal.p12
+forage.internal.tls.keystore.password=changeit
+forage.internal.tls.keystore.type=PKCS12
+forage.internal.tls.truststore.path=/certs/internal-ca.jks
+forage.internal.tls.truststore.password=trustme
+forage.internal.tls.client.authentication=REQUIRE
+
+# External API — trust only, no client cert
+forage.external.tls.truststore.path=/certs/external-ca.jks
+forage.external.tls.truststore.password=trustme
+```
+
+This registers two beans: `internal` and `external`. Reference them in routes:
+
+```yaml
+# mTLS to internal service
+- to:
+ uri: https://internal-api:8443/data
+ parameters:
+ sslContextParameters: "#internal"
+
+# Trust-only to external API
+- to:
+ uri: https://api.partner.com/v1/orders
+ parameters:
+ sslContextParameters: "#external"
+```
+
+## Cipher Suites
+
+Restrict the allowed cipher suites for TLS hardening:
+
+```properties
+forage.tls.cipher.suites=TLS_AES_256_GCM_SHA384, TLS_AES_128_GCM_SHA256
+```
+
+## Runtime Support
+
+`SSLContextParameters` is a core Camel class (`org.apache.camel.support.jsse`) — it works identically on plain Camel, Spring Boot, and Quarkus with no runtime-specific adapters needed.
diff --git a/website/mkdocs.yml b/website/mkdocs.yml
index 7a33696f4..383808169 100644
--- a/website/mkdocs.yml
+++ b/website/mkdocs.yml
@@ -125,6 +125,8 @@ nav:
- JMS: modules/jms.md
- Spring RabbitMQ: modules/spring-rabbitmq.md
- CXF: modules/cxf.md
+ - Security:
+ - TLS: modules/security/tls.md
- Guides:
- guides/index.md
- Camel JBang: guides/camel-jbang.md