Repository navigation
Event Bus
Canonical source:
docs/03-architecture/event-bus.md— this page is rendered from it on every publish; edit the source.
ClawAI uses RabbitMQ as its asynchronous event bus to decouple services and enable event-driven workflows. All inter-service events flow through a single topic exchange with routing-key-based filtering. The system includes dead-letter queues (DLQ) and retry mechanisms to ensure reliable message delivery.
+---------------------------+
| claw.events |
| (topic exchange, |
| durable) |
+---------------------------+
| | | | | |
+-----------------+ | | | | +------------------+
| | | | | |
v v | v v v
+---------------+ +----------+ | +---------+ +------------+ +----------+
| routing.queue | | chat.q | | | audit.q | | memory.q | | srvlog.q |
| bind: | | bind: | | | bind: | | bind: | | bind: |
| message. | | message. | | | *.# | | message. | | log. |
| created | | routed | | | | | completed | | server |
+-------+-------+ +----+-----+ | +----+----+ +-----+------+ +----+-----+
| | | | | |
v v | v v v
routing-service chat-service | audit-service memory-service server-logs
|
v
+-------+--------+
| DLQ exchange |
| claw.events.dlx|
| (direct) |
+-------+--------+
|
v
+-------+--------+
| DLQ queues |
| (per service) |
+----------------+
Published when a user sends a new chat message.
| Field | Type | Description |
|---|---|---|
messageId |
UUID | The newly created message ID |
threadId |
UUID | The thread this message belongs to |
userId |
UUID | The user who sent the message |
content |
string | The message text content |
forcedProvider |
string or null | User-forced provider (MANUAL_MODEL mode) |
forcedModel |
string or null | User-forced model |
fileIds |
UUID[] | IDs of attached files |
contextPackIds |
UUID[] | IDs of attached context packs |
threadRoutingMode |
RoutingMode | The thread's routing mode setting |
threadPreferredProvider |
string or null | Thread's preferred provider |
threadPreferredModel |
string or null | Thread's preferred model |
Publisher: chat-service Consumers: routing-service
Published when the routing engine has determined the optimal provider and model.
| Field | Type | Description |
|---|---|---|
messageId |
UUID | The message being routed |
threadId |
UUID | The thread ID |
selectedProvider |
string | Chosen provider (e.g., "anthropic", "local-ollama") |
selectedModel |
string | Chosen model (e.g., "claude-sonnet-4") |
confidence |
float | Routing confidence score (0.0 - 1.0) |
reasonTags |
string[] | Why this selection was made |
privacyClass |
PrivacyClass | LOW, MEDIUM, or HIGH |
costClass |
CostClass | LOW, MEDIUM, or HIGH |
fallbackProvider |
string or null | Fallback if primary fails |
fallbackModel |
string or null | Fallback model |
heuristicUsed |
boolean | Whether Ollama router was bypassed |
decisionId |
UUID | The recorded routing decision ID |
Publisher: routing-service Consumers: chat-service
Published when an AI response has been generated and stored.
| Field | Type | Description |
|---|---|---|
messageId |
UUID | The assistant message ID |
threadId |
UUID | The thread ID |
userId |
UUID | The user who initiated the request |
userContent |
string? | The original user message; absent on save turns (ADR-134) |
useMemory |
boolean? | Thread's memory switch; false = memory-service learns nothing from this turn (SEC-006) |
content |
string | The AI response |
provider |
string | Actual provider used |
model |
string | Actual model used |
routingMode |
RoutingMode | Routing mode that was active |
inputTokens |
number | Tokens in the prompt |
outputTokens |
number | Tokens in the response |
latencyMs |
number | Total execution time |
fallbackUsed |
boolean | Whether a fallback provider was used |
fallbackProvider |
string or null | Which fallback was used, if any |
fallbackModel |
string or null | Which fallback model, if any |
Publisher: chat-service Consumers: memory-service, audit-service, routing-service
Published when a new chat thread is created.
| Field | Type | Description |
|---|---|---|
threadId |
UUID | The new thread ID |
userId |
UUID | The user who created it |
title |
string | Thread title |
routingMode |
RoutingMode | Initial routing mode |
Publisher: chat-service Consumers: (currently no consumers; reserved for future analytics)
Published when a user successfully authenticates.
| Field | Type | Description |
|---|---|---|
userId |
UUID | The authenticated user |
email |
string | User email |
role |
UserRole | ADMIN, OPERATOR, or VIEWER |
sessionId |
UUID | The new session ID |
ipAddress |
string | Client IP (for audit) |
userAgent |
string | Client user agent |
Publisher: auth-service Consumers: audit-service
Published when a user logs out or their session is invalidated.
| Field | Type | Description |
|---|---|---|
userId |
UUID | The user who logged out |
sessionId |
UUID | The terminated session |
reason |
string | "manual", "expired", or "revoked" |
Publisher: auth-service Consumers: audit-service
Published after a temporary password has been successfully delivered via email, persisted by the auth service, and all active sessions for the target account have been revoked.
- Domain: identity
-
Publisher:
auth-service -
Consumers:
audit-service
| Field | Type | Description |
|---|---|---|
userId |
UUID | Target account that received the temporary password. |
issuedBy |
UUID | Administrator or system principal that issued the password. |
timestamp |
ISO-8601 | When the event was emitted. |
correlationId |
string (optional) | Trace correlation identifier for the issuing flow. |
This event intentionally does not carry the temporary password, any password hash, the user's email address, or a session token.
Published when a new cloud provider connector is configured.
| Field | Type | Description |
|---|---|---|
connectorId |
UUID | The new connector ID |
name |
string | Connector display name |
provider |
ConnectorProvider | Provider type |
userId |
UUID | User who created it |
Publisher: connector-service Consumers: audit-service
Published when a connector's configuration is modified.
| Field | Type | Description |
|---|---|---|
connectorId |
UUID | The updated connector ID |
name |
string | Connector display name |
provider |
ConnectorProvider | Provider type |
changes |
string[] | List of changed fields |
userId |
UUID | User who made the change |
Publisher: connector-service Consumers: audit-service
Published when a connector is removed.
| Field | Type | Description |
|---|---|---|
connectorId |
UUID | The deleted connector ID |
provider |
ConnectorProvider | Provider type |
userId |
UUID | User who deleted it |
Publisher: connector-service Consumers: audit-service
Published after a model sync operation completes for a connector.
| Field | Type | Description |
|---|---|---|
connectorId |
UUID | The connector that was synced |
provider |
ConnectorProvider | Provider type |
modelsAdded |
number | New models discovered |
modelsRemoved |
number | Models no longer available |
modelsUpdated |
number | Models with changed metadata |
totalModels |
number | Total models after sync |
Publisher: connector-service Consumers: audit-service, routing-service
Published after a connector health check completes.
| Field | Type | Description |
|---|---|---|
connectorId |
UUID | The checked connector |
provider |
ConnectorProvider | Provider type |
status |
string | "healthy", "degraded", or "unhealthy" |
latencyMs |
number | Health check response time |
error |
string or null | Error message if unhealthy |
Publisher: connector-service Consumers: audit-service, routing-service
Published after every routing decision for analytics.
| Field | Type | Description |
|---|---|---|
decisionId |
UUID | The routing decision ID |
messageId |
UUID | The routed message |
routingMode |
RoutingMode | Mode used |
selectedProvider |
string | Chosen provider |
selectedModel |
string | Chosen model |
confidence |
float | Confidence score |
heuristicUsed |
boolean | Whether heuristic fallback was used |
latencyMs |
number | Decision computation time |
Publisher: routing-service Consumers: audit-service
Published after a router model-knowledge sync run finishes. See routing-model-sync.md.
| Field | Type | Description |
|---|---|---|
runStartedAt |
string | ISO timestamp the sync run began |
runFinishedAt |
string | ISO timestamp the sync run finished |
durationMs |
number | Total run duration |
totals |
object | { upstreamCount, upsertedCount, skippedCount } |
perProvider |
array | Per-source { provider, status, upstream/upserted/skipped }
|
Publisher: routing-service Consumers: audit-service
Published after memories are extracted from a completed message.
| Field | Type | Description |
|---|---|---|
userId |
UUID | The user whose memories were extracted |
messageId |
UUID | Source message |
threadId |
UUID | Source thread |
memoriesCreated |
number | Count of new memories stored |
memoriesSkipped |
number | Count of duplicates skipped |
types |
string[] | Types of memories extracted (FACT, PREFERENCE, etc.) |
Publisher: memory-service Consumers: audit-service
Published when a file is successfully uploaded.
| Field | Type | Description |
|---|---|---|
fileId |
UUID | The uploaded file ID |
userId |
UUID | The uploader |
filename |
string | Original filename |
mimeType |
string | MIME type |
sizeBytes |
number | File size |
Publisher: file-service Consumers: (currently no consumers; reserved for future processing)
Published when a file has been chunked for context injection.
| Field | Type | Description |
|---|---|---|
fileId |
UUID | The chunked file ID |
chunkCount |
number | Number of chunks created |
totalCharacters |
number | Total character count across chunks |
Publisher: file-service Consumers: (currently no consumers; reserved for future indexing)
Published by all backend services for centralized log aggregation.
| Field | Type | Description |
|---|---|---|
level |
string | "debug", "info", "warn", "error" |
serviceName |
string | Originating service |
module |
string | NestJS module name |
action |
string | What was being done |
message |
string | Log message |
requestId |
string or null | X-Request-ID correlation |
traceId |
string or null | Distributed trace ID |
userId |
UUID or null | Associated user |
threadId |
UUID or null | Associated thread |
error |
object or null | Error details (stack, code) |
timestamp |
ISO 8601 | When the log was created |
Publisher: every backend service Consumers: server-logs-service
Events related to AI image generation lifecycle.
| Event | Description |
|---|---|
image.generation.started |
Image generation job has been queued |
image.generation.completed |
Image has been successfully generated |
image.generation.failed |
Image generation failed |
Publisher: image-service Consumers: audit-service
Max retries: 3
Retry delays:
Attempt 1: 1 second
Attempt 2: 5 seconds
Attempt 3: 30 seconds
Strategy: Exponential backoff with fixed delays
- Consumer receives a message and attempts processing
- If processing throws an exception:
a. Increment
x-retry-countheader b. If retry count < 3: republish to the same queue with a delay (using RabbitMQ delayed message plugin or TTL + dead-letter routing) c. If retry count >= 3: route to the DLQ - The DLQ (
claw.events.dlq.<service-name>) holds failed messages for manual inspection
Exchange: claw.events.dlx (direct, durable)
Queues:
claw.events.dlq.routing <- failed routing messages
claw.events.dlq.chat <- failed chat messages
claw.events.dlq.memory <- failed memory extraction
claw.events.dlq.audit <- failed audit logging
claw.events.dlq.server-logs <- failed log ingestion
Dead-letter queues should be monitored. Messages in the DLQ indicate:
- A consumer bug (most common)
- A downstream dependency failure (database, Ollama)
- A malformed event payload
Resolution: Fix the underlying issue, then replay messages from the DLQ.
- All event publishing uses the
RabbitMQServicewrapper fromshared-rabbitmq - If RabbitMQ is temporarily unreachable, the service logs a warning and continues (the HTTP response to the user is not blocked)
- Connection recovery is handled automatically by the amqplib client with reconnection backoff
- Each consumer wraps processing in a try-catch
- Known transient errors (database timeout, Ollama unreachable) trigger a retry via NACK + requeue
- Known permanent errors (invalid payload, missing required fields) trigger immediate DLQ routing (no retry)
- Unknown errors are retried up to the max retry count
Consumers should be idempotent because messages may be delivered more than once (at-least-once delivery). Key patterns:
- Memory extraction checks for duplicate memories before creating
- Audit logging uses
messageIdas a natural idempotency key - Routing decisions are keyed by
messageId(one decision per message)
RabbitMQ guarantees FIFO ordering within a single queue with a single consumer. Messages published to the same queue are delivered in order.
No ordering guarantees exist across different queues. For example, message.created and message.routed may be processed by their respective consumers in any relative order. The system handles this through:
-
Causal ordering via event chaining:
message.routedis only published AFTER the routing service has finished processingmessage.created. The chat service only processesmessage.routedafter it has already stored the user message. - State checks: Before processing an event, consumers verify that prerequisite state exists (e.g., the message record exists in the database).
If multiple instances of a service consume from the same queue, messages are distributed round-robin. This means messages for different threads may be processed in parallel, but messages for the same thread are NOT guaranteed to be in order across instances. For chat messages, this is acceptable because each message triggers a distinct pipeline.
The shared-rabbitmq package provides:
NestJS dynamic module for RabbitMQ connection setup. Handles:
- Connection string configuration
- Exchange declaration
- Queue declaration and binding
- Connection recovery
Service class for publishing events:
publish(routingKey: string, payload: unknown): Promise<void>- Automatic serialization to JSON
- Adds standard headers:
x-timestamp,x-source-service,x-request-id - Retry on publish failure (3 attempts)
Logger utility that publishes log.server events:
- Wraps NestJS Logger
- Automatically includes service name, module, request context
- Redacts sensitive fields before publishing
Added 2026-04-26 as part of the desktop-agent capability framework generalisation. Audit-service auto-subscribes to all 12 events.
| Event pattern | Publisher | Consumers | Purpose |
|---|---|---|---|
agent.capability.proposed |
claw-agent-service | audit | Capability invocation drafted; risk + class + target visible |
agent.capability.policy_matched |
claw-agent-service | audit | Risk service decided ALLOW / DENY / AUTO_APPROVE |
agent.capability.auto_approved |
claw-agent-service | audit, capability-runner | Skipped human approval per policy |
agent.capability.approved |
claw-agent-service | audit, capability-runner | Human approved |
agent.capability.rejected |
claw-agent-service | audit | Human rejected with reason |
agent.capability.executing |
claw-agent-service | audit | CLI started executing |
agent.capability.executed |
claw-agent-service | audit | Completed successfully with result |
agent.capability.failed |
claw-agent-service | audit | Execution errored |
agent.capability.cancelled |
claw-agent-service | audit | User cancelled mid-execute |
agent.capability.expired |
claw-agent-service | audit | PENDING beyond expiresAt |
agent.capability.rolled_back |
claw-agent-service | audit | Rollback executed (full or partial) |
agent.capability.denied |
claw-agent-service | audit | Risk service blocked at draft time |
Payload types: packages/shared-types/src/events/capability-events.types.ts.
ClawAI Wiki · source-controlled under wiki/ · generated/mirrored from the current repository knowledge layer.
ClawAI Wiki
Product
- Product-Vision
- Flagship-Features
- Business-Overview
- Requirements-Register
- Drift-Log
- Runtime-Managed-Connector-Providers
- Threads
- ADR-Index
Architecture
- System-at-a-Glance
- Architecture-Map
- System-Architecture
- Message-Flow
- End-to-End-Data-Flow
- Routing-Engine
- Event-Bus
- Security-Architecture
Platform
- Backend-Services
- Frontend-Architecture
- Shared-Packages
- Database-Reference
- API-Reference
- Provider-Catalog
- Memory-and-Context-Architecture
- File-Support-Architecture
- Research-and-Web-Crawling
Runtime & Ops
- Docker-Guide
- Nginx-Reference
- CI-CD-Pipeline
- Logging-and-Observability
- Operational-Runbooks
- Troubleshooting
Quality
Engineering OS
- AI-Native-Engineering-OS
- Generated-Manifests
- Rules-Catalog
- Skills-Catalog
- Context-Layer
- Engineering-Memory
- Tooling-and-Automation
- Scripts-and-Operations
- ESLint-and-Architecture-Enforcement
- Extended-Work-Skills
Agents
Reference