Skip to Content
ConceptsSIEM streaming (Sentinel, webhook)
Open in Claude Open in ChatGPT

Stream AgentValet to your SIEM

SIEM streaming sends your organisation’s governance events to your SIEM as they happen, so your security team sees agent activity next to identity, endpoint and cloud logs. Every event is in the OCSF 1.3  schema, and every batch is signed or authenticated, so your SIEM can trust where it came from.

Enterprise. SIEM streaming is included on the Enterprise plan. Organisation admins set it up in Settings → SIEM streaming. On other plans the page shows the upgrade option; see Pricing & plans.

Two destination types are available today:

  • Microsoft Sentinel, through the Azure Monitor Logs Ingestion API, into a table in your own workspace.
  • Webhook, an HTTPS endpoint you run (a collector, a SIEM’s HTTP input, or a small function), with every batch signed with HMAC-SHA256.

Splunk HEC, syslog/CEF and Shared Signals (CAEP) are not available yet.

What is sent

Every audit record AgentValet writes for your organisation becomes one OCSF event:

OCSF classclass_uidAgentValet events
API Activity6003Governed platform calls: allowed, denied and held for approval
Authorize Session3003Approval decisions and replays, AuthZEN and TRQP evaluations
Account Change3001Agents registered, issued as children, suspended, revoked, deleted, re-keyed; platforms connected
Entity Management3004Grants, delegates, connections, organisation settings and SIEM destinations changed
Authentication3002Agent binds and passkey use
Detection Finding2004Circuit breaker trips, anomaly suspensions, lost policy coverage
Base Event0Anything not in a class above

Each event carries OCSF metadata (product AgentValet, the event uid, the request’s correlation_uid), the actor (the agent and its organisation), the platform and operation, status, severity_id and time, plus an unmapped block with AgentValet ids: the policy, approval, grant (permission_id), parent agent and child issuance record.

Never sent: credentials, tokens or keys, request or response bodies, endpoint URLs (they can carry query strings), the agent’s free-text reason, upstream error text, email addresses, and IP addresses or other forensic fields. Every outbound field comes from an allow-list, and our test suite fails if any of these appear in a payload.

Filters

Each destination chooses:

  • Event classes: all of them, or only the ones you pick.
  • Minimum severity: from everything up to Critical only. Denials are Medium; circuit breaker trips and suspensions are High.
  • Routine allowed calls: off by default. Every allowed platform call is an event, and most SIEMs bill by the gigabyte. With it off you still get denials, held requests, approvals and lifecycle changes, subject to the two filters above.

How delivery works

  • Events are queued in the same transaction as the audit record, and sent within about a minute, in batches.
  • A failed batch is retried with exponential backoff (30 seconds, doubling, up to an hour, with jitter), honouring Retry-After. After the destination’s maximum attempts (8 by default) it is marked dead. Errors that retrying cannot fix, such as a refused signature or a missing table, go to dead straight away.
  • Retry dead on the destination puts its dead deliveries back in the queue, for example after you fix a receiver.
  • Delivery is at least once. If a batch is resent after a timeout or a partial failure, your SIEM may receive an event twice: deduplicate on the event uid (the EventUid column in Sentinel).
  • The delivery log on the settings page shows the latest 100 deliveries and their errors. Delivered records are kept 7 days, others 30.
  • Send test event sends one event to that destination only, ignoring its filters, so you can check a receiver end to end.

Microsoft Sentinel

AgentValet writes to a custom table, AgentValet_CL, in the Log Analytics workspace your Sentinel uses. Everything is created in your subscription by a template; AgentValet only holds an app registration’s client id and secret, and can only write to that one data collection rule.

1. Create an app registration

In the Entra admin centre, or with the Azure CLI:

az ad app create --display-name "AgentValet SIEM streaming" # note the appId (the client id), then: az ad sp create --id <appId> az ad sp show --id <appId> --query id -o tsv # the service principal object id, for step 2 az ad app credential reset --id <appId> --display-name agentvalet --years 1 # note the "password": this is the client secret, shown once

You need the Directory (tenant) ID, the Application (client) ID, the client secret’s Value, and the service principal’s object id.

2. Deploy the template

Download the template as Bicep or ARM JSON and deploy it into the resource group that holds your Sentinel workspace:

az deployment group create \ --resource-group <workspace-resource-group> \ --template-file agentvalet-sentinel.bicep \ --parameters workspaceName=<workspace-name> servicePrincipalObjectId=<object-id-from-step-1>

It creates:

  • the custom table AgentValet_CL (Analytics plan, your workspace’s retention);
  • a data collection rule of kind Direct, with its own logs ingestion endpoint (no data collection endpoint needed);
  • the Monitoring Metrics Publisher role for your app on that rule only.

Assigning that role needs Owner, User Access Administrator or Role Based Access Control Administrator on the resource group. If you only have Contributor, add assignRole=false to the parameters, and have someone with one of those roles grant it afterwards:

az role assignment create --assignee-object-id <object-id-from-step-1> --assignee-principal-type ServicePrincipal --role "Monitoring Metrics Publisher" --scope <dataCollectionRuleId output>

The deployment prints logsIngestionEndpoint (for example https://dcr-agentvalet-ab12-australiaeast.logs.z1.ingest.monitor.azure.com), dcrImmutableId, streamName and dataCollectionRuleId.

3. Add the destination

In Settings → SIEM streaming, choose Add destination, then Microsoft Sentinel, and enter the tenant id, client id, client secret, and the three outputs. When you save, AgentValet asks Microsoft Entra for a token with those credentials, so a wrong tenant, client id or secret is refused straight away. The client secret is stored encrypted and never shown again.

4. Send a test event

Choose Send test event. A new role assignment can take up to 30 minutes to take effect, and the first data in a new table can take up to 15 minutes to appear. Until the role is active, deliveries show HTTP 403 and are retried up to the destination’s maximum attempts (about an hour at the default of 8), then marked dead; use Retry dead once the role is active. Then query:

AgentValet_CL | where TimeGenerated > ago(1h) | project TimeGenerated, ClassName, Severity, Status, AgentId, PlatformId, Operation, Reason, EventUid | order by TimeGenerated desc

Columns

The key fields are flat for KQL, and the whole OCSF event is in Ocsf (dynamic):

ColumnTypeFrom
TimeGenerateddatetimetime
EventUidstringmetadata.uid (deduplicate on this)
ClassUid, ClassName, CategoryUidint, string, intthe OCSF class
ActivityId, ActivityName, TypeUidint, string, longthe OCSF activity
SeverityId, Severityint, stringseverity_id
StatusId, Status, Messageint, string, stringthe outcome
AgentId, ParentAgentId, OrgId, OwnerIdstringwho acted
PlatformId, Operation, Result, Reasonstringwhat was attempted and the decision
CorrelationUid, PolicyId, ApprovalId, PermissionIdstringAgentValet ids to join on
OcsfVersionstringmetadata.version
Ocsfdynamicthe whole event

AgentValet’s tests check every record against the column list in this template, so the two cannot drift apart. Azure public cloud only for now.

Troubleshooting

  • HTTP 403 in the delivery log: the role assignment has not propagated yet (wait up to 30 minutes), or it was removed. Deliveries are retried until the destination’s maximum attempts; after that, use Retry dead.
  • entra: invalid_client AADSTS7000215: the client secret is wrong or expired. Edit the destination and paste a new one.
  • HTTP 404 (or 403): the DCR immutable id or stream name may not match the deployed rule. Compare them with the template outputs.
  • HTTP 400: the table or rule was changed by hand and no longer matches the template. Redeploy the template.

Webhook

AgentValet POSTs each batch to your HTTPS URL (public, port 443, no query string):

{ "schema": "ocsf", "schema_version": "1.3.0", "count": 2, "events": [ { "class_uid": 6003, "metadata": { "uid": "..." } } ] }

with these headers:

HeaderValue
X-AgentValet-TimestampUnix seconds when the batch was signed
X-AgentValet-Signaturev1= + hex HMAC-SHA256 of <timestamp>.<raw body>, keyed with your signing secret
User-AgentAgentValet-SIEM/1

Answer with any 2xx. AgentValet retries 408, 425, 429 and 5xx; other 4xx responses and redirects go to dead.

Verify the signature

Compute the HMAC over the raw body exactly as received (before parsing), compare in constant time, and refuse a timestamp more than five minutes from your clock so an old batch cannot be replayed.

import { createHmac, timingSafeEqual } from "node:crypto"; export function verifyAgentValet(rawBody, headers, secret) { const ts = Number(headers["x-agentvalet-timestamp"]); if (!Number.isInteger(ts) || Math.abs(Date.now() / 1000 - ts) > 300) return false; const expected = "v1=" + createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex"); const got = String(headers["x-agentvalet-signature"] ?? ""); return got.length === expected.length && timingSafeEqual(Buffer.from(got), Buffer.from(expected)); }
import hmac, hashlib, time def verify_agentvalet(raw_body: bytes, headers, secret: str) -> bool: ts = int(headers.get("X-AgentValet-Timestamp", "0")) if abs(time.time() - ts) > 300: return False msg = f"{ts}.".encode() + raw_body expected = "v1=" + hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest() return hmac.compare_digest(headers.get("X-AgentValet-Signature", ""), expected)

The signing secret is generated in the settings page (or pasted, at least 32 characters) and is never shown again after you save. To rotate it, edit the destination and enter a new one.

Limits and what is not covered yet

  • Splunk HEC, syslog/CEF over TLS and Shared Signals (CAEP) are not available yet.
  • Some actions do not write an audit record today, so they are not streamed: changes to policies and policy rules, disconnecting or deleting a platform, agent self-diagnostic reports, and refused agent sign-ins.
  • Up to 10 destinations per organisation.
  • After a downgrade from Enterprise, streaming stops at once. Destinations stay listed, and you can delete them.
Last updated on