SIEM & Webhooks
DVARA writes every security-relevant event to an append-only, HMAC-signed, hash-chained audit log. For compliance and incident response, you'll almost certainly need to ship those events to a SIEM or fan them out to webhooks. This page covers both.
SIEM export
DVARA ships three SIEM exporters out of the box: Splunk HEC, AWS CloudWatch Logs, and Apache Kafka. Each exporter subscribes to the audit event stream and forwards events as they are written. Any combination can be enabled simultaneously — events are dispatched to every active exporter, and failure on one exporter never blocks the others or the primary audit persistence.
Splunk HEC
Splunk's HTTP Event Collector receives audit events as JSON over HTTPS. DVARA POSTs to the HEC endpoint using the configured token and index.
dvara:
llm-gateway:
siem:
splunk:
enabled: true
hec-url: https://splunk.example.com:8088/services/collector
token: ${SPLUNK_HEC_TOKEN}
index: dvara-audit # blank = default index
source: dvara-gateway
source-type: _json
timeout-seconds: 10
| Property | Default | Description |
|---|---|---|
enabled | false | Enable Splunk export |
hec-url | — | Splunk HEC endpoint |
token | — | HEC authentication token |
index | blank | Target Splunk index (blank = HEC default) |
source | dvara-gateway | Splunk source field |
source-type | _json | Splunk sourcetype field |
timeout-seconds | 10 | HTTP timeout |
The source-type: _json default tells Splunk to parse events as structured JSON, so every DVARA audit field becomes a searchable attribute. No Splunk-side field extraction required.
AWS CloudWatch Logs
CloudWatch Logs batches events and flushes to a log group + log stream on a schedule. DVARA uses the AWS SDK under the hood and honors the default credential provider chain (IRSA on EKS, instance profile on EC2, env vars otherwise).
dvara:
llm-gateway:
siem:
cloudwatch:
enabled: true
log-group: /dvara/audit
log-stream: dvara-audit
region: us-east-1
endpoint: # blank = default regional endpoint
batch-size: 25
timeout-seconds: 10
| Property | Default | Description |
|---|---|---|
enabled | false | Enable CloudWatch export |
log-group | — | CloudWatch Logs group name (must exist) |
log-stream | dvara-audit | Log stream name |
region | us-east-1 | AWS region |
endpoint | blank | Custom endpoint URL (for GovCloud, etc.) |
batch-size | 25 | Events per PutLogEvents call |
timeout-seconds | 10 | AWS SDK timeout |
IAM permissions — the task / pod identity must have logs:CreateLogStream and logs:PutLogEvents on the target log group.
Apache Kafka
Kafka is the highest-throughput option — use it when your SIEM is a Kafka consumer (Fluentd, Logstash, Vector) or when you need a durable buffer in front of your SIEM. DVARA publishes to Kafka with SASL and SSL authentication support.
dvara:
llm-gateway:
siem:
kafka:
enabled: true
bootstrap-servers: broker1.example.com:9092,broker2.example.com:9092
topic: dvara-audit
dead-letter-topic: dvara-audit-dlq
acks: all
security-protocol: SASL_SSL
sasl-mechanism: SCRAM-SHA-512
sasl-jaas-config: >-
org.apache.kafka.common.security.scram.ScramLoginModule required
username="dvara" password="${KAFKA_PASSWORD}";
| Property | Default | Description |
|---|---|---|
enabled | false | Enable Kafka export |
bootstrap-servers | — | Comma-separated broker list |
topic | dvara-audit | Primary target topic |
dead-letter-topic | blank | DLQ for delivery failures (blank = DLQ disabled) |
acks | all | Producer acks — all waits for ISR replication, protecting audit events from broker failures |
security-protocol | — | e.g. SASL_SSL, PLAINTEXT |
sasl-mechanism | — | e.g. PLAIN, SCRAM-SHA-256, SCRAM-SHA-512 |
sasl-jaas-config | — | Full JAAS config string |
The default acks=all is deliberate: audit events must survive broker failures. Dropping this to acks=1 or acks=0 improves throughput but risks silent data loss — don't do it for compliance use cases.
Multi-destination export
Every exporter runs independently. Enabling all three ships each event to Splunk, CloudWatch, and Kafka in parallel — useful for migration scenarios or when different teams own different pipelines.
Webhooks
Webhooks are the outbound side of DVARA's event system — HTTPS callbacks fired when specific events occur (policy decisions, budget breaches, approval gates, MCP tool calls, etc.). They're per-tenant and managed via the DVARA Flightdeck or the Automation API.
Each webhook has:
- URL — target HTTPS endpoint
- Event types — which DVARA event types to subscribe to. Webhook fan-out is selective, not general: it covers a fixed set of 12 operational and security events, not every audit event the gateway writes. The full set (see the
WebhookEventTypeenum) isPOLICY_DENIAL,PII_DETECTED,IP_ACCESS_DENIED,BUDGET_CAP_SOFT,BUDGET_CAP_HARD,BUDGET_WARNING,INJECTION_DETECTED,GUARDRAIL_BLOCKED,AGENT_LOOP_DETECTED,MCP_APPROVAL_REQUESTED,MCP_APPROVAL_TIMEOUT, andCOST_ANOMALY. Subscribe with these names; two of them ship on theX-Gateway-Eventdelivery header in a slightly different form —POLICY_DENIALarrives asPOLICY_DENIEDandBUDGET_WARNINGarrives asBUDGET_CAP_WARNING. If you route on the header value (rather than parsing the JSON body), match both forms or filter on the body'seventType. If you need a fan-out for events outside this set (TENANT_CREATED,GATEWAY_RESPONSE, etc.), use SIEM export instead — SIEM ships every audit event the gateway writes. - Secret — HMAC-SHA256 signing secret, sent as the
X-Gateway-Signature: sha256=<hex>header - Status —
ACTIVEorDISABLED
:::note SSRF protection on tenant-supplied URLs
A webhook URL is fetched server-side by DVARA. When a tenant registers a
webhook through tenant self-service, the URL is validated at save time: it
must be an http/https endpoint that does not resolve to a loopback,
private, link-local, or cloud-metadata address (e.g. 127.0.0.1,
10.0.0.0/8, 192.168.0.0/16, 169.254.169.254). This blocks
server-side request forgery — a tenant cannot turn webhook delivery into a
probe of your internal network or cloud metadata. As defense-in-depth,
operators should also run a network egress policy so the gateway can only
reach intended destinations.
Scope: this validation runs on the tenant portal registration path
only. The platform admin REST endpoint (POST /v1/admin/webhooks, owner
role) deliberately does not validate — it's a privileged path used for
operator tooling like internal monitoring webhooks. Scripts that accept
arbitrary URLs and call the admin endpoint should add their own URL
allowlist; otherwise an owner-scoped PAT can register a webhook to any
address the gateway can reach.
:::
Retry and delivery
Webhook delivery uses exponential backoff with a bounded retry count. Global config under dvara.llm-gateway.webhooks.*:
| Property | Default | Description |
|---|---|---|
enabled | true | Master switch |
max-retries | 3 | Max delivery retry attempts |
base-retry-delay-ms | 1000 | Initial retry delay |
retry-multiplier | 4 | Exponential backoff multiplier (1s → 4s → 16s → 64s) |
delivery-timeout-ms | 5000 | HTTP timeout per attempt |
max-delivery-log-entries | 50000 | Total delivery log cap (oldest evicted) |
approval-base-url | blank | Base URL used to build approval action links in MCP webhook payloads |
approval-ttl-minutes | 15 | TTL for HMAC-signed approval tokens |
Every delivery attempt (success or failure) is recorded in the delivery log. The DVARA Flightdeck surfaces this log per webhook so you can debug 502s, timeouts, and signature mismatches.
Payload headers and signing
Every POST carries three headers:
| Header | Purpose |
|---|---|
X-Gateway-Signature: sha256=<hex> | HMAC_SHA256(secret, rawBody). Your receiver must verify this before trusting the payload. |
X-Gateway-Event: <event-type> | The DVARA event type (one of the 12 above). Use this to route on the receiver side without parsing the body. |
X-Gateway-Delivery: <uuid> | Stable delivery ID. Same value across every retry of the same event, so it doubles as a natural idempotency key. |
A reference signature-verification implementation in Python:
import hashlib, hmac
def verify(request, secret):
signature = request.headers.get("X-Gateway-Signature", "")
if not signature.startswith("sha256="):
return False
expected = hmac.new(
secret.encode(),
request.body, # raw bytes — must be pre-parse
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature[len("sha256="):], expected)
The signature is over the raw request body bytes as sent on the wire. If your receiver framework parses and re-serializes JSON before handing it to your code (common in some middleware stacks), capture the raw body upstream — re-serialized JSON will not byte-match and signature verification will fail.
Approval webhooks (MCP)
When an MCP tool call hits an approval gate, DVARA fires an approval webhook with signed action links so a human approver can click "Approve" or "Deny" directly from a Slack / email message. The links are HMAC-signed bearer tokens with a TTL of approval-ttl-minutes (default 15) and resolve via POST /v1/webhooks/actions/{action}?token=<signed-token>.
Configure approval-base-url to your public gateway URL so the links are clickable from outside your cluster.
Admin API
| Endpoint | Purpose |
|---|---|
POST /v1/admin/webhooks | Create webhook |
GET /v1/admin/webhooks | List (filter by ?tenant_id=) |
PUT /v1/admin/webhooks/{id} | Update |
DELETE /v1/admin/webhooks/{id} | Delete |
POST /v1/admin/webhooks/{id}/test | Send a synthetic test event |
GET /v1/admin/webhooks/{id}/deliveries | Per-webhook delivery log |
Use the test endpoint when onboarding a new webhook to validate signature verification on the receiver side before going live.
Error codes
webhook_not_found→ HTTP404,type: not_found_error
Related
- Observability — Prometheus metrics and the audit event system overview
- Compliance Reports — SOC2 / HIPAA / GDPR report generation, which consumes the same audit chain
- Configuration — full property reference