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