Availability
| Edition | Deployment Type |
|---|---|
| Enterprise | Self-Managed, Hybrid |
Why Compliance Events
Before v2.1, the only thing the compliance dashboard could count was 4xx proxy logs, that is, requests the gateway blocked. That misses the most common governance activity in practice: a filter quietly redacts an email address, rewrites a passage of text, or flags something suspicious while still allowing the request through. Those interventions are exactly what compliance teams need visibility into, and they previously left no trace beyond a debug log line. Compliance events fix that. Any filter script can attach a list of structured events to its output. The platform persists them, propagates them from edge gateways back to the control plane, exposes them via API and dashboard, and counts them in Prometheus, all without changing the filter’s block/allow decision.Event Schema
A compliance event has the following shape (ComplianceEventOutput in the script API):
| Field | Type | Required | Description |
|---|---|---|---|
event_type | string | Yes | Free-form type. Suggested conventions: pii_redacted, content_rewritten, policy_violation, harmful_content_detected, silent_failure. |
severity | string | No | info, warning, or critical. Defaults to info if omitted or invalid. |
description | string | No | Human-readable description of what happened, shown in the dashboard event row. |
metadata | map | No | Arbitrary key-value data. Stored as JSON. Conventions: matched_pattern (string of the trigger), redacted_types (array of category names). |
- Events without an
event_typeare silently skipped. This avoids polluting the table with empty records when a script branch builds the event partially. severityvalues outsideinfo|warning|criticalare coerced toinfo.event_typeon the API query side is limited to 100 characters; any longer is a 400.severityon the API query side is validated against the same whitelist; an invalid value is a 400.- All API queries use GORM parameterized queries, so SQL injection attempts in
event_typeorseverityare treated as literal string matches and return empty results.
Server-Side Enrichment
Scripts only declare the four fields above. The platform enriches each event at recording time with context from the filter execution site:| Enriched field | Source |
|---|---|
app_id | App that owns the request (proxy paths). 0 for chat-session paths, where user_id is the principal. |
user_id | Authenticated user (chat-session paths). |
llm_id | LLM in scope for the request. |
vendor | LLM vendor string (for example openai, anthropic, bedrock). |
model_name | Model being called. |
filter_name | Name of the filter that emitted the event. |
filter_scope | Where the script ran (see below). |
timestamp | Wall-clock time at recording. |
Filter Scopes
filter_scope identifies which of the six filter execution sites recorded the event:
| Scope | Site |
|---|---|
proxy_request | LLM proxy request filters (before reaching the LLM). |
proxy_response | LLM proxy response filters (REST and streaming). |
chat_request | Chat-session message filters (before RAG search and LLM). |
chat_response | Chat-session response filters (regular and streaming). |
file_reference | File content filters (before RAG indexing). |
tool_response | Tool response filters (after tool execution). |
filter_scope indirectly via the dashboard drill-down, or by joining compliance_events.filter_name against your filter catalog.
Emitting Events from a Filter Script
In a Tengo filter script, setcompliance_events on the output object:
Behavior
- Recording is asynchronous. Events are queued and written by the analytics pipeline, so filter latency is unaffected.
- Compliance events never affect the block/allow decision. Setting
block: falsewith critical events is legitimate (the filter chose to redact rather than block); settingblock: truewith no events is also legitimate. - The list can be empty or omitted. Most filters won’t emit events on every invocation; typically only when a condition fires.
- Events are recorded per filter, per invocation. A single request can produce multiple events from multiple filters.
Edge Gateways
In a hub-spoke deployment, filter scripts run on the Edge Gateway, but compliance events live on the control plane. The propagation path is:- Filter script on the edge sets
compliance_eventsin its output. - The Edge Gateway’s analytics handler (
MicrogatewaAnalyticsHandler.RecordComplianceEvents) queues the events to the pulse plugin’s local buffer, which survives a gateway restart. - On the next analytics pulse (every 30 seconds by default), events are batched into the gRPC
AnalyticsPulsemessage asComplianceEventProtoentries. - The control server’s
SendAnalyticsPulsehandler reconstructs the compliance event from each entry, preservingLLMID,AppID,UserID, severity, type, description, metadata, vendor, and model name, and persists it.
aistudio_compliance_events_total counter on the control-plane /metrics endpoint and group by source.
Querying Compliance Events
Admin API
| Param | Type | Description |
|---|---|---|
start_date | ISO8601 date | Lower bound (inclusive) on timestamp. |
end_date | ISO8601 date | Upper bound (inclusive) on timestamp. |
app_id | uint | Filter by app. |
event_type | string (max 100 chars) | Exact match on event_type. |
severity | info / warning / critical | Whitelist; other values return 400. |
limit | int | Page size (default and max are deployment-dependent). |
offset | int | Page offset. |
metadata is returned as a JSON-encoded string (the storage shape). Parse it client-side.
SQL
The underlying table iscompliance_events. Useful indexes:
(app_id, timestamp): composite, for per-app time-bounded queries.user_id,llm_id,filter_name,event_type,severity: single-column, for filtering.timestamp: for retention sweeps.
Dashboard Surfaces
The Compliance dashboard (/admin/compliance) was extended in v2.1 to surface compliance events alongside the existing blocked-request data.
Summary Cards
Two new cards next to the blocked-request total:- Critical Events: count over the selected window, with trend arrow. Escalates the card style above
COMPLIANCE_EVENTS_CRITICAL_ESCALATE_AT(default 1, since a single critical event already warrants attention). - Warning Events: count over the selected window, with trend arrow. Escalates above
COMPLIANCE_EVENTS_WARNING_ESCALATE_AT(default 20, since warnings are advisory and need to accumulate before they matter).
Policy Violations Tab
The summary is split into three counters:- Blocked: 4xx proxy logs (existing).
- Flagged: warning and critical compliance events that passed through (new).
- Affected Apps: distinct apps appearing in either source.
Filter Events Tab
A dedicated tab for compliance events with:- Severity totals (info / warning / critical) with a stacked timeline chart.
- Severity and event-type filters (the event-type dropdown is populated from observed values in the window).
- Pagination over the event list.
- Expandable rows showing the raw
metadataJSON. - CSV export wired to
GET /compliance/events.
App Risk Modal
Per-app risk score now includes event counts, withwarningEventWeight = 1 and criticalEventWeight = 3, so a single critical event contributes the same to the score as three warning events. Recent Violations interleaves blocked requests and compliance events sorted by timestamp, with severity-aware row rendering.
Metrics
A Prometheus / OpenTelemetry counter is exposed on the/metrics endpoint:
aistudio_llm_requests_total to get a per-request rate.
See Analytics & Monitoring for the full metric catalog.
Suggested Event-Type Conventions
event_type is free-form, but consistency across filters makes dashboards and queries much more useful. Conventions used by the bundled filter scripts:
| Event type | When to emit |
|---|---|
pii_redacted | Filter replaced PII patterns with placeholders. Set metadata.redacted_types to an array of categories (["email", "ssn", "phone"]). |
content_rewritten | Filter transformed user or LLM content in a way the user can’t see (for example system-prompt augmentation, anonymization). |
sensitive_content_detected | Filter found content matching a sensitive pattern. Pair with block: true for hard blocks; pair with block: false to record the detection without blocking. |
harmful_content_detected | Response filter detected harmful output. Set metadata.matched_pattern to the trigger. |
policy_violation | Filter detected a policy breach that may or may not be blocked. |
silent_failure | Filter encountered an error path it chose to swallow (for example an unreachable classifier service, falling back to allow). Use severity: warning so it shows up in the dashboard without paging anyone. |
metadata keys, prefer:
matched_pattern(string): the substring or regex match that fired the event.redacted_types(array of strings): categories of PII or content removed.
Migration Notes
- Schema: v2.1 GORM auto-migration creates the
compliance_eventstable on first startup. No manual step required. - Filter scripts written for v2.0 are forward-compatible. The
compliance_eventsfield is optional and ignored if absent. - Bundled filter library: the ship-with scripts (PII redaction, content blocking, response guardrails) were updated in v2.1 to emit compliance events with the conventions above. Re-import them from the marketplace, or copy from the updated templates in the admin UI, if you want them out of the box.
- Custom analytics handlers: the
AnalyticsHandlerinterface now takescontext.Contexton its recording methods, including the newRecordComplianceEvents. Custom implementations need to update their signatures;RecordComplianceEventscan be a no-op if you do not consume compliance events.
See Also
- Filters: script-level syntax and examples.
- Analytics & Monitoring: the surrounding analytics pipeline and the full metric catalog.
- Edge Gateway: hub-spoke architecture and the analytics pulse that carries edge events to control.
- Tyk AI Studio release notes: the
AnalyticsHandlercontext change and related v2.1.0 SDK updates.