Skip to content

Observability / SIEM

Every governed call, detection, policy verdict, and tool invocation is written to the TapPass audit trail. The SIEM export pipeline ships those events downstream so they live alongside the rest of your security telemetry.

Destination destination value Formats Go to
Splunk HEC splunk_hec CEF, JSON Splunk HEC
Azure Sentinel azure_sentinel JSON Azure Sentinel
Generic webhook webhook JSON, CEF, OCSF Webhooks
audit events ──▶ format (CEF | OCSF | JSON) ──▶ filter ──▶ redact ──▶ buffer/batch ──▶ destination
  • Formatcef for SIEMs that parse it natively (Splunk, ArcSight); ocsf for OCSF consumers; json for everything else.
  • Filter — minimum severity (all | detection | action | block) and event-type include/exclude lists.
  • Redact — API keys, tokens, connection strings, and absolute file paths are stripped automatically before export; large content fields are truncated.
  • Buffering — events are flushed in batches (configurable size / interval) with retries and exponential backoff.

The export is enabled by setting a SIEM URL:

Terminal window
TAPPASS_SIEM_URL=https://splunk.example.com:8088/services/collector
TAPPASS_SIEM_DESTINATION=splunk_hec # splunk_hec | azure_sentinel | webhook
TAPPASS_SIEM_AUTH_TOKEN=<hec-token>

Or at runtime via the settings API (stored, takes effect immediately):

  • GET /api/settings/siem — current configuration (token redacted)
  • PUT /api/settings/siem — update configuration
  • POST /api/settings/siem/test — send a test event to verify connectivity
  • GET /api/settings/siem/stats — export pipeline statistics

Example update:

Terminal window
curl -X PUT https://tappass.example.com/api/settings/siem \
-H "Authorization: Bearer $ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"destination": "splunk_hec",
"url": "https://splunk.example.com:8088/services/collector",
"auth_token": "<hec-token>",
"format": "cef",
"severity_filter": "detection"
}'

Alongside the SIEM pipeline, TapPass can export every governance decision as an OpenTelemetry span — so decisions land in the same tracing backend as the rest of your system (Datadog, Honeycomb, Grafana Tempo, or any OTLP collector), correlated by the standard trace attributes.

This is a second sink on the same audit stream. Each span carries the decision’s verdict, reason, rule id, behavior type, agent/org, model, tokens, and cost; blocked decisions surface as span errors.

Enable it with two environment variables — the standard OTLP endpoint plus the opt-in flag:

Terminal window
OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317
TAPPASS_OTLP_AUDIT_SPANS=1

Backends are pure configuration — point OTEL_EXPORTER_OTLP_ENDPOINT (and OTEL_EXPORTER_OTLP_HEADERS) at your collector; there is no per-vendor code.

It’s off by default on purpose: the gateway and MCP server already emit live spans for the calls they trace, so turn this on when you want uniform OTLP coverage of every decision — including direct /v1/govern and chat — not just those two paths.

The OTEL_EXPORTER_OTLP_ENDPOINT above is a deployment-wide setting — one collector for the whole server, ideal for self-hosting and for TapPass’s own operational telemetry. In a multi-tenant SaaS deployment, each org instead configures its own destination, so company A streams its governance decisions to A’s Datadog and company B to B’s Honeycomb — same install, separate backends.

Per-tenant destinations are org-scoped config, managed via the settings API (org-admin):

Endpoint Purpose
GET /api/settings/export-destinations List this org’s destinations (auth tokens redacted to ****last4)
POST /api/settings/export-destinations Add a destination (kind, endpoint, secret_header, token, event_filter, and format for SIEM kinds)
PUT /api/settings/export-destinations/{id} Update — echo the masked token to leave it unchanged
DELETE /api/settings/export-destinations/{id} Remove it (and its stored secret)
POST /api/settings/export-destinations/{id}/test Route a test decision to verify delivery

How it stays safe:

  • Strict isolation — routing keys on the decision’s authoritative org_id; a tenant only ever receives its own decisions.
  • Encrypted secrets — auth tokens are stored encrypted at rest in the credential vault, never in plaintext; the API only returns the last four characters.
  • SSRF-guarded — tenant-supplied endpoints are validated (DNS-resolving, fail-closed), so a destination can’t point at internal or metadata addresses.
  • Fail-safe — a destination whose token is missing is skipped, never sent unauthenticated.

Both destination families are delivered end-to-end:

  • otlp — spans to the org’s OpenTelemetry backend (Datadog / Honeycomb / Grafana Tempo / any collector).
  • SIEMsplunk_hec, azure_sentinel, webhook, each with a format (cef / ocsf / json). The token is the destination’s own auth material (Splunk HEC token, Sentinel shared key, webhook secret); events are formatted and redacted through the same pipeline the global SIEM export uses, so secrets never leave in payloads.

Two layers coexist. The deployment-wide sinks — the OTEL_EXPORTER_OTLP_ENDPOINT collector and the super_admin-configured global SIEM — are TapPass’s own operator telemetry (Layer 1): they see every org’s decisions, for platform monitoring. Per-tenant destinations (Layer 2) are each org’s own: strictly isolated by org_id, so a tenant only ever receives — and authenticates as — itself.

For real-time notifications on governance events, TapPass can also fire a webhook to Slack, Teams, or a generic endpoint:

Terminal window
TAPPASS_ALERT_WEBHOOK_URL=https://hooks.slack.com/services/T.../B.../xxx
TAPPASS_ALERT_WEBHOOK_TYPE=slack # slack | teams | generic
TAPPASS_ALERT_MIN_SEVERITY=block # block | detect | all

Configurable at runtime via GET/PUT/POST /api/settings/alerting (plus /test and /delivery-log). This is fire-and-forget and never blocks the governance path. This deployment-wide webhook is the operator’s own channel (Layer 1, super_admin).

Each org can also route its own governance alerts to its own Slack/Teams/generic webhook — a Layer-2 peer to the deployment-wide webhook above. Org-admins manage them via the settings API:

Endpoint Purpose
GET /api/settings/alert-destinations List this org’s alert destinations (webhook URLs masked to ****last4)
POST /api/settings/alert-destinations Add one (webhook_type, webhook_url, min_severity)
PUT /api/settings/alert-destinations/{id} Update — echo the masked URL to leave it unchanged
DELETE /api/settings/alert-destinations/{id} Remove it (and its stored secret)
POST /api/settings/alert-destinations/{id}/test Send a test alert to verify delivery
GET /api/settings/alert-destinations/{id}/delivery-log This org’s recent delivery outcomes

Same isolation guarantees as export destinations: routed strictly by the decision’s org_id, webhook URLs stored encrypted in the vault (the API returns only the last four characters), SSRF-guarded, and each org’s delivery log is separate — an org only ever sees its own alerts. Each destination has its own min_severity (all/detect/block), so one org can get every detection while another gets only blocks.