Operate
SIEM export
The audit stream is the same stream regardless of where it ends up — the operator console, the built-in reports, and your SIEM all see identical event envelopes. This page is the reference for wiring the stream into a downstream pipeline, the schema guarantees it ships with, and the common sinks Wardengate supports out of the box.
Event envelope
Every event is a single JSON object with a stable envelope. Event-type specific fields live under context. The envelope is the contract; new top-level fields are additive and documented per release.
{
"ts": "2026-04-20T18:41:02.419Z", // RFC 3339, UTC, ms precision
"event_type": "session.opened", // dotted namespace, stable
"event_id": "evt_01HZ8F4N92Y7EJG1K5T3RCMN8W",
"sequence": 48210411, // monotonic per cluster
"actor": {
"id": "usr_01HX3KQ9FR",
"email": "alice@corp",
"groups": ["sre", "oncall-primary"],
"kind": "user"
},
"target": {
"id": "tgt_01HX4MZ01V",
"name": "web-01.prod",
"protocol": "ssh",
"address": "10.10.4.20:22",
"tags": { "env": "prod", "role": "web" }
},
"result": "success", // success | denied | error
"session_id": "sess_a91c7f20bb48",
"policy": "prod-ssh-write",
"gateway": "gw-us-1",
"source_ip": "198.51.100.24",
"context": {
"account": "deploy",
"mfa": "webauthn",
"approvals": [{ "approver": "carol@corp", "at": "2026-04-20T18:40:55Z" }]
},
"prev_hash": "8a71f0...c411",
"hash": "5f8ab4...d027"
}Event catalog
The event-type namespace is stable across releases. A shortened catalog:
| Namespace | Events |
|---|---|
auth.* | sso_login, sso_failed, mfa_challenge, mfa_passed, session_signed_in |
session.* | requested, approved, opened, closed, terminated, shadowed, handoff |
policy.* | allowed, denied, matched, changed, deployed |
command.* | allowed, denied, alerted, approved, filtered |
recording.* | sealed, viewed, exported, expired, legal_hold |
idp.* | scim_create, scim_update, scim_disable, sync_failed |
system.* | gateway_registered, gateway_drained, config_changed, license_updated |
Supported sinks
Sinks are declared with an EventSink resource. Multiple sinks can run in parallel — fanning out to a primary SIEM and a cheap archive is the typical setup.
Splunk HEC
apiVersion: wardengate/v1
kind: EventSink
metadata:
name: splunk-primary
spec:
kind: splunk-hec
endpoint: https://splunk.corp.example:8088/services/collector/event
tokenSecret: splunk-hec-token
index: wardengate
sourcetype: wardengate:v1
batch:
maxEvents: 500
maxBytes: 1mb
flushSeconds: 2Elastic / OpenSearch
spec:
kind: elastic
endpoint: https://elastic.corp.example:9200
indexTemplate: wardengate-{yyyy.MM.dd}
auth: { kind: apiKey, secret: elastic-api-key }
mapping: ecs # elastic common schema
pipeline: wardengate_enrichDatadog
spec:
kind: datadog
endpoint: https://http-intake.logs.datadoghq.com/api/v2/logs
apiKeySecret: datadog-api-key
ddsource: wardengate
ddtags: ["env:prod", "service:wardengate"]Sumo Logic
spec:
kind: sumo-http
collectorUrlSecret: sumo-collector-url
category: wardengate/prodChronicle
spec:
kind: chronicle
customer: "00000000-0000-0000-0000-000000000000"
region: us
serviceAccountSecret: chronicle-sa
logType: WARDENGATE
mapping: udm # Unified Data ModelSyslog (RFC 5424)
spec:
kind: syslog
transport: tls # tcp | udp | tls
host: siem.corp.example
port: 6514
facility: local4
format: rfc5424 # rfc3164 also supported
framing: octet-count
messageFormat: cef # cef | json | raw
ca: /etc/wardengate/tls/corp-ca.pemAWS Kinesis
spec:
kind: kinesis
region: us-east-1
stream: wardengate-audit
partitionKey: "{{ .actor.id }}"
credentials: { kind: irsa, roleArn: arn:aws:iam::111:role/wg-kinesis }Kafka
spec:
kind: kafka
brokers: ["kafka-0:9093", "kafka-1:9093", "kafka-2:9093"]
topic: wardengate.audit.v1
auth: { kind: sasl-scram-512, secret: kafka-wg }
tls: { caSecret: kafka-ca, skipVerify: false }
acks: all
compression: zstdWebhook
spec:
kind: webhook
url: https://siem.corp.example/ingest/wardengate
headers:
Content-Type: application/x-ndjson
Authorization: "Bearer {{ secret:siem-token }}"
signing:
algorithm: hmac-sha256
secret: siem-webhook-signing
header: X-Wardengate-SignatureBackpressure and delivery guarantees
Each sink has an in-memory batch buffer and an on-disk spool that takes over when the buffer fills. Events flow in this order:
- In-memory batch (default 500 events or 1 MB, whichever first).
- On-disk spool if the upstream is slow or unreachable (default 2 GB, configurable).
- Dead-letter queue for events that the sink rejects repeatedly (4xx-class failures), inspectable through
wgctl sink dlq lsand replayable withwgctl sink dlq replay.
Sinks are at-least-once by default. The envelope event_id is idempotent — your SIEM can dedup on it. For sinks that cannot dedup, switch the sink to deliveryMode: exactly-once and Wardengate takes on the cost of transactional batching.
Dead-letter queue
The DLQ catches events that the upstream rejected for reasons that will not fix themselves (malformed, forbidden, bad index). Network errors and 5xx responses are retried with exponential backoff and never hit the DLQ. DLQ events are kept for 14 days and surfaced as an alert when the count crosses a configurable threshold — “my SIEM ate 4000 events and I had no idea” is the exact pain mode this exists to eliminate.
Field mappings for common schemas
The raw envelope maps cleanly to the common normalisation schemas your SIEM already knows. The sink config selects the schema and Wardengate emits pre-mapped fields — your SIEM engineers do not need to write parsers.
CEF (Common Event Format)
CEF:0|Wardengate|WGCP|1.6|session.opened|Session opened|3|\
src=198.51.100.24 suser=alice@corp duser=deploy \
dst=10.10.4.20 dpt=22 app=ssh \
cs1Label=policy cs1=prod-ssh-write \
cs2Label=sessionId cs2=sess_a91c7f20bb48OCSF (Open Cybersecurity Schema Framework)
| Wardengate | OCSF |
|---|---|
actor.email | actor.user.email_addr |
target.address | dst_endpoint.ip/dst_endpoint.port |
source_ip | src_endpoint.ip |
session_id | session.uid |
result | status / status_id |
ECS (Elastic Common Schema)
| Wardengate | ECS |
|---|---|
ts | @timestamp |
event_type | event.action |
actor.email | user.email |
source_ip | source.ip |
target.address | destination.ip / destination.port |
Sample events
policy.denied
{
"ts": "2026-04-20T19:03:11.802Z",
"event_type": "policy.denied",
"event_id": "evt_01HZ8FC7R2Y7EJG1K5T3RCMN8W",
"actor": { "email": "mallory@corp", "groups": ["contractors"] },
"target": { "name": "billing-ro.prod", "protocol": "postgres", "tags": {"env":"prod"} },
"result": "denied",
"policy": "default-deny",
"context": {
"reason": "no allow rule matched",
"source_ip": "203.0.113.77",
"attempted_account": "readonly"
}
}command.denied
{
"ts": "2026-04-20T19:05:24.017Z",
"event_type": "command.denied",
"session_id": "sess_a91c7f20bb48",
"actor": { "email": "alice@corp" },
"target": { "name": "web-01.prod", "protocol": "ssh" },
"result": "denied",
"context": {
"rule": "block-disk-wipes",
"cmd": "rm",
"argv": ["-rf", "/*"],
"policy": "prod-ssh-rails"
}
}recording.exported
{
"ts": "2026-04-20T19:08:41.122Z",
"event_type": "recording.exported",
"session_id": "sess_a91c7f20bb48",
"actor": { "email": "carol@corp", "groups": ["auditors"] },
"result": "success",
"context": {
"format": "mp4",
"bytes": 14872131,
"sha256": "7b4e...c910",
"reason": "ticket SEC-0412"
}
}idp.sync_failed
{
"ts": "2026-04-20T19:10:01.000Z",
"event_type": "idp.sync_failed",
"result": "error",
"context": {
"provider": "corp-okta",
"kind": "scim",
"http_status": 401,
"error": "invalid_token",
"retry_at": "2026-04-20T19:15:00Z"
}
}Testing the pipeline
wgctl sink test splunk-primary
connect ............... ok (tls 1.3)
auth .................. ok
write 10 synthetic events .... ok (ack in 84ms)
backlog ............... 0 events / 0 B on-disk
last error ............ (none in last 24h)Related
- Audit & reporting — the same event stream, displayed inside Wardengate.
- Alerts — firing rules off the event stream before it leaves the cluster.
- Webhooks — a simpler programmatic consumer when a full SIEM sink is overkill.