Audit Log Streaming (SIEM)
Stream every Controlinfra audit event to one or more SIEMs (Splunk, Datadog, Elastic, Sumo Logic, or any HTTP collector) in real time. Useful for satisfying SOC 2 / ISO 27001 audit-trail requirements that ask for centralised log retention and alerting.
Plan Requirement
Audit log streaming is an Enterprise feature.
How It Works
Every time Controlinfra writes an audit log entry (a member is added, a guardrail is deployed, a scan is triggered, an SSO config changes, etc.), it fans the same record out to every enabled destination as a JSON payload over HTTPS POST. Datadog and Splunk side-by-side is a normal config — many SOC teams send to one tool for ops dashboards and another for compliance retention.
Delivery is fire-and-forget per destination — the audit write itself is never blocked, and one slow destination doesn't slow down the others. Failed deliveries are logged and surfaced as a per-destination error on the settings page, but they do not back up.
Configuring Destinations
Each destination is one row on the settings page. You can have as many as you need (the typical setup is 1–3).
- Navigate to Settings → Security & quality → Audit log streaming
- Click + Add destination
- Give it a descriptive Name (e.g. "Datadog SecOps", "Splunk Compliance") — this only shows up in the UI
- Pick the Provider preset — this drives the form's helper text and the icon shown on the destination card. Functionally every preset uses the same JSON-over-HTTPS dispatch, so picking the wrong one isn't fatal.
- Paste the Webhook URL (must be HTTPS)
- (Optional) Paste an Authorization header if your SIEM expects one
- Save, then click Send test on the row to verify
Webhook URL + Authorization header are encrypted at rest with AES-256-GCM. The UI shows a redacted preview after save (host + path visible, sensitive query params masked) — the original plaintext is never returned.
Per-row controls
Each destination has its own Enable / Disable, Send test, Edit, and Delete buttons. Toggling one destination doesn't affect the others.
HTTPS only
Webhook URLs must use https://. Plain http://, localhost, private IP ranges, and cloud metadata endpoints (169.254.169.254, etc.) are rejected to prevent SSRF.
Provider Examples
Datadog
Pass the API key as the dd-api-key query string parameter. Controlinfra's destination form only supports a single Authorization header — arbitrary custom headers like DD-API-KEY aren't supported, so use the URL approach:
Webhook URL:
https://http-intake.logs.datadoghq.com/api/v2/logs?ddsource=controlinfra&service=audit&dd-api-key=<YOUR_DD_API_KEY>
Authorization header: (leave blank — key is in the URL)Region notes:
- US1 →
http-intake.logs.datadoghq.com - US3 →
http-intake.logs.us3.datadoghq.com - EU →
http-intake.logs.datadoghq.eu
In Datadog, search service:audit source:controlinfra to see the events.
Splunk HEC
Webhook URL: https://<your-splunk>:8088/services/collector/event
Authorization header: Splunk <YOUR_HEC_TOKEN>Splunk HEC's /services/collector/event endpoint reserves the top-level event JSON key as the wrapper for the actual log content. When the destination type is set to Splunk, Controlinfra automatically wraps the standard payload as the HEC shape:
{
"event": {
"event": "controlinfra.audit",
"schemaVersion": 1,
"action": "scan.triggered",
"actor": { ... },
"target": { ... },
"metadata": { ... }
},
"sourcetype": "_json",
"source": "controlinfra-audit",
"time": 1714000000
}So in Splunk Search, the actual log fields are nested under event.*. Useful queries:
source="controlinfra-audit" event.action="scan.triggered"
source="controlinfra-audit" event.actor.email="user@example.com"
source="controlinfra-audit" event.target.type="guardrail"If you'd rather index the payload flat (no event.* prefix), use /services/collector/raw instead — it accepts the same wrapped shape but Splunk treats the inner JSON as the indexed log directly.
Elastic / Elasticsearch
Use the Filebeat HTTP input or an Elastic Agent listening on an HTTP endpoint:
Webhook URL: https://<your-elastic-ingest>/logs
Authorization header: ApiKey <YOUR_BASE64_KEY>Sumo Logic
Create an HTTP Logs & Metrics Source in Sumo and copy the URL:
Webhook URL: https://endpoint<n>.collection.<region>.sumologic.com/receiver/v1/http/<token>
Authorization header: (leave blank — token is in the URL)Generic webhook
Any HTTPS endpoint that accepts Content-Type: application/json will work. Respond with 2xx to mark delivery successful; any other status (or a timeout > 5s) is recorded as a failure.
Payload Schema
Each event is POSTed as a single JSON object:
{
"event": "controlinfra.audit",
"schemaVersion": 1,
"occurredAt": "2026-05-06T18:42:11.214Z",
"orgId": "65a1f2c3...",
"userId": "65a1f2c3...",
"actor": {
"id": "65a1f2c3...",
"email": "user@example.com",
"name": "Jane Doe"
},
"action": "guardrail.deployed",
"description": "Deployed guardrail \"Prod S3 deny-public\" to AWS account 123456789012",
"target": {
"type": "guardrail",
"id": "65b9e0...",
"name": "Prod S3 deny-public"
},
"metadata": {
"cloud": "aws",
"accountId": "123456789012",
"region": "us-east-1"
},
"ipAddress": "203.0.113.10"
}Fields:
| Field | Description |
|---|---|
event | Always "controlinfra.audit" — useful for routing in your SIEM |
schemaVersion | Increments only on breaking changes |
occurredAt | ISO 8601 UTC timestamp |
orgId | Source organization (string, may be null for system events) |
userId | Acting user (null for system / scheduled actions) |
actor | Snapshot of the user (id / email / name) at event time |
action | Dot-notation action key (scan.triggered, member.role_changed, etc.) |
description | Human-readable summary |
target | What was acted on — type / id / display name |
metadata | Action-specific extras (varies by action) |
ipAddress | Source IP of the request that triggered the event |
schemaVersion will only ever increase on breaking changes. We add new optional fields without bumping it, so build dashboards defensively (treat unknown fields as additive).
Delivery Guarantees & Limits
- At-most-once delivery, no retries. If your endpoint is down for 30 seconds, those events will not be replayed (they remain in the Controlinfra audit log itself, so you can backfill via the API).
- 5-second timeout per request. Slow endpoints are dropped, not queued.
- Order: no delivery ordering guarantee. Because dispatch is async and not serialized across concurrent audit writes, your SIEM may receive events out of order. Always sort by
occurredAton the receiver side. - No PII expansion — secrets, tokens, and credentials are masked or redacted before they reach the audit log itself, so they never appear in the streamed payload either.
Security
- Webhook URL and Authorization header are encrypted at rest with AES-256-GCM (key derived via scrypt from
ENCRYPTION_KEY). - The UI only ever shows a masked "configured" indicator — the original values are not retrievable.
- SSRF protection: webhook URLs must be HTTPS, must not resolve to localhost / link-local / RFC 1918 / cloud-metadata IPs.
- Last-delivery status (timestamp + error) is shown on the settings page so you can confirm the integration is alive.
Troubleshooting
"Test event delivered (HTTP 202)" but events not visible in Datadog Check service:audit source:controlinfra and widen the time range. Datadog can take up to 30 seconds to index newly-ingested logs.
Last delivery shows HTTP 401 Authorization header is wrong or missing. For Datadog, double-check the API key and pass it via the dd-api-key URL query parameter. Do not put DD-API-KEY <key> in the header field, because that field maps to the standard Authorization header, not Datadog's DD-API-KEY header.
Last delivery shows HTTP 403 Your SIEM rejected the request. Splunk HEC tokens often need the matching index/sourcetype permissions; Elastic API keys must include write on the target index.
Last delivery shows request timeout or fetch failed The endpoint took longer than 5 seconds, or DNS / TLS handshake failed. Confirm the URL resolves publicly (Controlinfra cannot reach private endpoints).
No events at all, including system events New destinations are enabled by default after you save them. If nothing is arriving, confirm the destination still shows as enabled on the settings page and check the last-delivery status for an HTTP / DNS / TLS error.
Related
- Audit Logging — what gets logged and how to query it
- Notifications Setup — for alerting humans (Slack / email) rather than feeding a SIEM