Send AI Agent Audit Logs to Wazuh
Part of SIEM audit forwarding. Decision Terrain writes audit events as JSON Lines files that an enrolled Wazuh agent or manager collects. It then confirms delivery by searching your Wazuh indexer.
Prepare Wazuh
You need an enrolled agent or manager that can read the audit output, an alert-processing path (Filebeat), and a searchable indexer. Create a separate read-only indexer query user and note the trusted CA. Approve access for every Organization you forward.
Installation staff first assigns an enrolled collector route to your Organization in System Settings → External security deployment, under Enrolled Wazuh routes by Organization (JSON). You select that route when adding the destination. The assignment makes the route available in the form; your installation operator must separately configure the collector's filesystem access.
Ask your operator for this destination's output directory, which is <organization>/<destination>/collectors/<route>/ under the bridge's storage root. Mount only that directory read-only at /var/log/decisionterrain/audit inside the collecting agent or manager, matching the supplied localfile template. If you mount it elsewhere, update the template's <location> to that collector-visible path followed by /segment-*.jsonl. Match the bridge output group to the collector's read group: directories use mode 0750 and segment files use 0640. Do not expose another destination's files, another Organization's directory, the bridge database or credentials. Your operator's installation documentation covers the operator setup.
Configure collection and rules
On the enrolled agent or manager, add the supplied localfile template inside ossec_config. It watches the numbered segment-*.jsonl files with JSON decoding and reads from the start of each file (only-future-events=no), with recovery bounded to 64 MB. Reload the relevant service and start collection before sending a synthetic test or activating the destination. Wazuh documents file collection and localfile recovery options; check their behavior on your installed version, especially newly discovered or rotated files.
On the manager, install the bridge alert rules. Confirm rule IDs 100210–100213 are free; these rules replace the older sample rules with overlapping IDs, so do not load both. Validate representative lines with wazuh-logtest, then reload your rules through your normal Wazuh process.
The base rules raise level-3 alerts for both families, and deny or failure outcomes raise level 7. Make sure your alert threshold admits level 3, Filebeat forwards alerts, and your index pattern is searchable. Keep active responses off for this ingestion setup.
Before choosing Send synthetic test, ensure file collection, the manager's bridge rules, and Filebeat/indexer routing are running. The synthetic test uses this same file-to-indexer path, so saving or validating the destination alone does not prepare Wazuh to receive it.
Wazuh's built-in JSON decoder exposes the event as dynamic fields, so you do not need a handwritten decoder. Check the keyword mappings for data.EventName, data.OrganizationId and data.EventId on your indexer version, because Decision Terrain searches those exact fields to confirm delivery.
Add the destination
In Governance → Policies → External security → Audit forwarding, choose Add destination and select Wazuh. Select the staff-assigned route in Enrolled Wazuh collector, then enter the HTTPS indexer endpoint, the index pattern, the read-only query username and password, and the trusted CA. If no collector is available, ask installation staff to assign one to your Organization; you cannot enter a filesystem path in the destination form. Save, validate, send a synthetic test and activate collection. See SIEM audit forwarding.
Synthetic tests, activation and other destination controls require independent human security-policy approval. A pending request has not taken effect; after a different authorized administrator approves it, the original requester must repeat the exact action.
Verify indexed fields
Generate real product events for each family you selected, then search the actual alert data for the family, Organization and event ID. For an authenticated read-only indexer search, the request is:
POST /wazuh-alerts-*/_search
{
"size": 100,
"query": {
"bool": {
"filter": [
{"term": {"data.EventName": "organization.security"}},
{"term": {"data.OrganizationId": "<organization-uuid>"}},
{"term": {"data.EventId": "<event-uuid>"}}
]
}
}
}
Use your index pattern. For agent events, use agent.audit and the corresponding event ID. Inspect the raw _source.data rows and compare every projected field, including the agent run, sequence and hash, available model metadata, and optional security service, agent and work identifiers.
Wazuh's top-level timestamp is the manager's processing time. Use data.OccurredAt for source timelines. Numeric fields arrive as decimal strings because the dynamic decoder returns strings. A missing optional value is different from a valid zero. Keep the raw duplicate rows before you group by EventName, OrganizationId and EventId.
Starter detection rules
Install wazuh-detection-rules.xml beside the bridge rules, and confirm IDs 100220–100259 are free. It requires wazuh-bridge-rules.xml (100210–100213) and does not replace it. High-signal events alert at level 10 or 12. Repeated sign-in or credential failures use frequency rules (five and three within ten minutes for the same account or credential). The rules cover the conditions in SIEM audit forwarding. Review the thresholds before relying on them.
The frequency rules count matching deliveries, not unique audit events. They group sign-in and credential failures by ResourceId, and agent policy denials by AgentId, without deduplicating EventId. Retried deliveries can therefore inflate counts and trigger an alert even when fewer distinct events occurred. Deduplicating later search results does not undo an alert already triggered. Test duplicate deliveries when tuning these rules. If your detection requires a threshold of unique events, implement it in an indexer monitor or external checker that deduplicates by EventName, OrganizationId and EventId before counting by the relevant account, credential or agent.
Heartbeat absence monitoring
The supplied manager rules do not detect silence. Configure a scheduled indexer monitor or an external checker using an explicit list of Organizations expected to forward security events to this destination. Heartbeats require an active destination that includes the security audit family. They default to once an hour; the operator can change the interval or disable them.
Maintain the expected list when Organizations start or intentionally stop forwarding. Create one check per listed Organization, rather than deriving the list from recent documents or a terms aggregation. This keeps checks running for Organizations that never reported or have been silent for more than 24 hours.
For example, run the following search against your alert index pattern every five minutes, replacing the UUID for each expected Organization:
POST /wazuh-alerts-*/_search
{
"size": 0,
"track_total_hits": true,
"query": {
"bool": {
"filter": [
{"term": {"data.EventName": "organization.security"}},
{"term": {"data.OrganizationId": "<organization-uuid>"}},
{"term": {"data.Action": "siem_heartbeat"}},
{"range": {"data.OccurredAt": {"gte": "now-2h", "lte": "now"}}}
]
}
}
}
This example requires data.OccurredAt to be mapped as a date field so date math works. Verify the actual index mapping first; do not assume a dynamically mapped string supports this query. If necessary, have your indexer administrator configure the index template and create a correctly mapped index; changing an existing field's type requires reindexing. Verify exact-match mappings for the three term fields as well.
Alert for that Organization when a successful, complete search returns hits.total.value == 0. A timeout, failed shard, missing index, authentication failure or invalid mapping is a monitoring failure, not a healthy result. Configure a separate notification for check failures. The monitor's account must be able to search all relevant destination indices.
Use data.OccurredAt, not the manager's top-level timestamp, to assess freshness: a replayed old heartbeat must not clear the alert merely because Wazuh processed it recently. Adjust the two-hour window for your configured interval and indexing delay. Test both an Organization with no heartbeat and a replayed old heartbeat before relying on the monitor.
Troubleshoot
| Observation | Check |
|---|---|
| Audit files are not collected | The read-only mount path, group and file permissions, the wildcard, and that collection started before activation |
| Decoder works but no indexed alerts | Rules loaded, free rule IDs, the level-3 threshold, and Filebeat and index routing |
| Query authentication or TLS failure | The separate read-only indexer credentials and the trusted CA |
| Query finds no known event | The index and data.* field mappings, initial-file latency, and the event identity |
| Delivered records appear again | Decision Terrain resubmits after a grace period when a search does not find an event. Deduplicate by event identity |
An indexer outage never proves a file was not ingested. Do not rotate files by copy-truncation or remove files that are not yet confirmed to free disk space.