Routes¶
Routes are the heart of KETE. They define what events go where.
What is a Route?¶
Think of a route like a mail forwarding rule:
"When I get a LOGIN event from the master realm, convert it to JSON and send it to Kafka."
Every route has five parts:
| Part | What it does | Required? |
|---|---|---|
| Realm Matchers | Which realms to listen to | No |
| Event Matchers | Which events to forward | No |
| Serializer | What format to use | No (JSON default) |
| Destination | Where to send events | ✅ Yes |
| Retry | Retry on failure | No |
How Routes Work¶
flowchart LR
E["📥 Event"] --> M{"🔍 Match?"}
M -->|No| D["🗑️ Skip"]
M -->|Yes| S["📝 Format"]
S --> Dest["🚀 Send"]
- Event arrives from Keycloak
- Serializer formats — the event is serialized once per distinct serializer (JSON, XML, etc.)
- Matchers filter — does this route want this event?
- Destination sends — deliver to Kafka, RabbitMQ, etc.
Quick Example¶
Send login events to Kafka:
kete.routes.logins.event-matchers.filter=glob:LOGIN*
kete.routes.logins.destination.kind=kafka
kete.routes.logins.destination.bootstrap.servers=kafka:9092
kete.routes.logins.destination.topic=login-events
Without matchers, a route accepts all events from all realms.
Disabling Routes¶
Routes are enabled by default. To disable a specific route:
To disable KETE entirely, see Enabling & Disabling.
Delivery Semantics¶
- Delivery happens after the Keycloak transaction commits, asynchronously on a virtual-thread executor — events never block the Keycloak request, and routes never see events from rolled-back transactions.
- Routes are independent: a failing route does not affect the others, and there is no ordering guarantee across routes.
- Delivery is best-effort: after the route's retry attempts are exhausted the event is logged at
WARNand dropped (there is no dead-letter queue). - A route whose destination is unreachable at start-up logs
WARN Failed to initialize route : <name> (will retry in the background)and is retried after 30 seconds, then at doubling intervals up to every 5 minutes, until it initializes; events for that route are not delivered until then andkete.routes.failedcounts it. A route whose configuration is invalid is dropped permanently. The remaining routes start normally.
Route Properties¶
| Property | Default | Description |
|---|---|---|
kete.routes.<name>.enabled |
true |
Enable/disable the route |
kete.routes.<name>.realm-matchers.<id> |
(all realms) | Realm matcher — see Matchers |
kete.routes.<name>.realm-match-mode |
any |
any or all |
kete.routes.<name>.event-matchers.<id> |
(all events) | Event matcher — see Matchers |
kete.routes.<name>.event-match-mode |
any |
any or all |
kete.routes.<name>.serializer.kind |
json |
Serializer — see Serializers |
kete.routes.<name>.serializer.* |
- | Serializer-specific properties |
kete.routes.<name>.destination.kind |
(required) | Destination — see Destinations |
kete.routes.<name>.destination.* |
- | Destination-specific properties |
kete.routes.<name>.destination.authentication-type |
- | Destination authentication method (values are destination-specific) |
kete.routes.<name>.destination.headers.<header> |
- | Custom headers (eventkind, eventtype, contenttype are reserved and ignored) |
kete.routes.<name>.destination.content-encoding |
- | gzip or deflate — see Content Encodings |
kete.routes.<name>.destination.content-transfer-encoding |
- | base64 — see Content Transfer Encodings |
kete.routes.<name>.destination.tls.* |
- | TLS/mTLS — see TLS & mTLS |
kete.routes.<name>.destination.pool.* |
- | Destination pool — see Destination Pool |
kete.routes.<name>.retry.* |
- | Retry — see Retry |
Multiple Routes¶
Define as many routes as you need. Each works independently:
# Route 1: Everything to Kafka
kete.routes.all.destination.kind=kafka
kete.routes.all.destination.bootstrap.servers=kafka:9092
kete.routes.all.destination.topic=all-events
# Route 2: Errors to webhook
kete.routes.alerts.event-matchers.filter=glob:*_ERROR
kete.routes.alerts.destination.kind=http
kete.routes.alerts.destination.url=https://alerts.example.com/hook
Match Modes¶
When you have multiple matchers, you can control how they combine:
| Mode | Behavior |
|---|---|
any |
Event matches if any matcher accepts it (default) |
all |
Event matches only if all matchers accept it |
# Match mode for realm matchers
kete.routes.strict.realm-match-mode=all
# Match mode for event matchers
kete.routes.strict.event-match-mode=all
Example: Match Multiple Event Types (any)¶
# Match LOGIN OR LOGOUT events (default mode is 'any')
kete.routes.auth.event-matchers.login=glob:LOGIN*
kete.routes.auth.event-matchers.logout=glob:LOGOUT*
kete.routes.auth.destination.kind=kafka
kete.routes.auth.destination.bootstrap.servers=kafka:9092
kete.routes.auth.destination.topic=auth-events
Example: Require All Conditions (all)¶
# Match only events that match BOTH patterns
kete.routes.strict.event-match-mode=all
kete.routes.strict.event-matchers.must-be-login=glob:LOGIN*
kete.routes.strict.event-matchers.must-not-error=glob:not:*_ERROR
kete.routes.strict.destination.kind=http
kete.routes.strict.destination.url=https://api.example.com/events
Retry¶
Retry is enabled by default (3 attempts, 500ms between retries). Customize retry settings per route:
See Retry for full configuration options and duration formats.