Skip to main content

Alerts and audit

A guardrail violation produces two independent records: an alert delivered to the Levo platform, and an audit line written to the gateway's stdout for your own log pipeline. They are not the same thing and they do not currently cover the same events.

Alerts in the Levo dashboard

Violations appear under Issues → AI Governance, scoped to the environment selected in the switcher.

ColumnMeaning
SeverityCritical / High / Medium / Low, from the finding
Alert titleWhat fired — the detector or scanner name
ActionWhat the gateway did: blocked, masked, or flagged
OriginWhich policy produced it
ApplicationThe traffic this was seen on
DirRequest or response leg
UserThe identity attributed to the call, when one was extracted
First seen / Last seenDeduplication window bounds
StatusTriage state

Alerts fire in MONITOR mode too. A monitored policy that blocks nothing still raises the same alert, tagged with the enforcement mode it came from, which is what makes a monitor-mode rollout measurable.

Repeated identical findings are deduplicated rather than emitted per request — First seen and Last seen are the bounds of that window.

The audit record

Set logFullAudit: true and the gateway writes one structured JSON line per request to stdout, under the ai_gateway_audit target. Ship stdout with your existing log shipper; no separate exporter is needed.

config:
policies:
logFullAudit: true

A blocked request looks like this:

{
"timestamp": "2026-09-02T04:31:12.900Z",
"request_id": "8cdb2887-ff56-4dac-9f9a-25eb03811657",
"source_ip": "10.0.4.19",
"source_identity": null,
"user_id": null,
"user_email": null,
"destination_host": "api.openai.com",
"resolved_upstream": "api.openai.com:443",
"destination_path": "/v1/chat/completions",
"method": "POST",
"user_agent": "curl/8.5.0",
"classification": {
"category": "llm_provider", "provider": "openai",
"confidence": 1.0, "matched_rule": null
},
"governance": {
"decision": "AllowWithInspection", "blocked": false,
"matched_policy": "inspect-all-traffic", "reason": null
},
"inspection": {
"action": "Reject",
"finding_count": 1,
"highest_severity": "high",
"findings": [
{
"category": "InjectionHeuristics",
"finding_type": "InjectionHeuristics",
"severity": "high",
"scanner": "InjectionHeuristics",
"description": "LLM Bastion scanner 'InjectionHeuristics' blocked request content"
}
]
},
"action": {
"decision": "Block",
"actions_taken": [
{ "name": "inspection-reject", "description": "Request blocked by inspection findings" }
],
"headers_injected": 0,
"body_rewritten": false
},
"model_name": "gpt-4o-mini",
"total_latency_ms": 9.3,
"classification_latency_us": 6,
"governance_latency_us": 66,
"inspection_latency_us": 9276
}

A masked request carries "action": "Masked" and "body_rewritten": true, with the finding's byte offsets:

  "inspection": {
"action": "Masked",
"finding_count": 1,
"highest_severity": "critical",
"findings": [
{ "category": "pii", "finding_type": "ssn", "severity": "critical",
"scanner": "unified-pii", "description": "ssn detected",
"location": { "start": 71, "end": 82 } }
]
}

The fields worth building detections on:

FieldUse it for
request_idJoining the request and response lines for one call. Also returned in the body of a block and injected as a response header
inspection.actionAllow, Flagged, Masked, Reject
inspection.highest_severityAlert routing
inspection.findings[].finding_typeThe specific pattern or scanner
governance.decisionWhether inspection ran at all — a stream of "Allow" with "inspection": null means nothing is being scanned
action.decisionPass or Block

That fifth row is the one to build a dashboard panel on. A governance decision of Allow together with "inspection": null is the machine-readable form of the governance gate problem.

Keeping lines a sane size

A large prompt can produce hundreds of findings on one line.

SettingEffect
logFindingsMinSeverityOnly findings at or above this severity are written into findings[]
logFindingsMaxPerLineCaps how many are written
config:
policies:
logFullAudit: true
logFindingsMinSeverity: high
logFindingsMaxPerLine: 20

Filtering affects the log line only. Everything still counts in finding_count, still sets highest_severity, and still raises an alert — so a filtered line cannot hide a violation, only its detail.

Known gap: the response leg writes no findings

Response-leg findings do not reach a log-based SIEM

The request leg writes the full record above. The response leg writes only a thin line:

ai_gateway_audit  upstream response
request_id=3d86dd84-… destination=api.openai.com:443
status_code=403 latency_ms=2.21
response_snippet="{\"error\":\"blocked\",\"reason\":\"InjectionHeuristics detected
in LLM response\",\"policy\":\"response-inspection\",\"direction\":\"response\"}"
event_type="upstream_response"

There is no findings array, no finding_count, no highest_severity and no category. Response-side findings — which is where indirect prompt injection is caught — are delivered to the Levo platform as alerts and are visible under Issues → AI Governance, but they do not appear in the audit log in a form a SIEM can parse as a finding.

Until this is closed, detect response-leg blocks on the shape of the thin line instead: event_type="upstream_response" together with status_code=403 and a response_snippet containing "direction":"response". That tells you a block happened and which request it belongs to, but not which scanner fired or at what severity.

This page will be updated when the response leg emits a full record.

A worked example

Ship stdout to your SIEM and these are the two detections worth having on day one.

Something was blocked.

event_type != "upstream_response" AND action.decision = "Block"
| stats count by inspection.findings{}.finding_type, source_ip

Nothing is being inspected. A steady rate of this over a window where you expect scanning means the governance gate is not open:

governance.decision = "Allow" AND isnull(inspection)
| timechart count

For the Guardrail API rather than the proxy, the same records are written for input_type: request calls. See Guardrail API.

Was this page helpful?