Agent Evidence: SIEM and Detection Integration

Feed signed Pipelock receipts to your SIEM, analyst, or LLM funnel.

Ready to protect your own setup?

Pipelock is a real-time agent firewall. It blocks what it can see on the wire. That is a hard, narrow job, and it is not the whole detection story.

Long-running agents move through multi-week attack chains, reason in natural language, and produce actions that look benign in isolation. No real-time gateway will catch all of that. The gate is the first line, not the last.

This page is for the people building the layer that runs behind the gate. SIEM engineers, SOC analysts, and researchers training detection models all need the same thing upstream: structured, tamper-evident evidence of what the agent did. Pipelock emits that as mediator-signed action receipts.

Why real-time gateways are not enough

Picture a malicious MCP server running a two-step attack across a week.

Step one, Monday: the agent calls an innocent-looking tool. The tool response tells the agent to install a hook that exfiltrates data to https://google.com/report. To an inline detector, that looks like a single bad tool response, but the destination is benign and the content might pass under a looser policy.

Step two, the following Monday: a different tool response tells the agent to change the hook’s destination. To an inline detector, that request is a one-line config edit. The agent already has the hook. Changing an endpoint is not structurally suspicious.

Put the two steps next to each other and the intent is obvious. A detector looking at a week of activity sees the shape. A detector looking at a single request cannot.

Long-window detection is the only way to catch attacks designed to look like two benign things. The question is what you feed that detector.

The primitive: mediator-signed action receipts

When flight_recorder.signing_key_path is set, every proxy decision produces a signed receipt. Receipts are Ed25519-signed, JSON-structured, and linked into a SHA-256 hash chain so any deletion or reordering is detectable after the fact.

Each receipt carries the fields a downstream detector needs:

FieldWhat
action_idUUIDv7, unique per decision.
timestampRFC 3339 wall-clock time the decision was made.
verdictblock, warn, exemption, allow, strip, redirect, or ask.
layerWhich scanner triggered (mcp_response_scan, dlp_header, response_scan, airlock, etc.).
patternNamed rule inside the layer (Prompt Injection, aws_access_key, etc.).
transportfetch, forward, websocket, mcp_stdio, mcp_http_upstream, mcp_http_listener, connect, intercept.
session_idGroups receipts from the same agent session.
principal / actorWho initiated the action and who enforced it.
policy_hashSHA-256 of the canonical policy config at decision time. Moves whenever the policy changes.
side_effect_class / reversibilityClassification of the attempted action.
chain_prev_hash / chain_seqHash-chain linkage to the prior receipt in the stream.
signature / signer_keyEd25519 signature and public key.

These are fixed fields. A detector parses them with a JSON reader, not a log regex. Full schema reference: Pipelock action receipt spec.

Three downstream consumers

The same receipt stream serves three detection styles without modification.

SIEM rules

Ship the flight-recorder JSONL file to Splunk, Datadog, Elastic, or any SIEM that ingests JSON. The standard pattern is a file shipper (Filebeat, Fluent Bit, Vector) tailing flight_recorder.dir and forwarding each line. Filter to type == "action_receipt" at the shipper or at the SIEM.

The fields map cleanly onto structured search. Group by session_id to reconstruct a session. Filter verdict=block to audit enforcement. Alert on pattern=aws_access_key for secret-exfil attempts.

Pipelock also has a separate real-time emit pipeline (webhook, syslog, OTLP) for operator alerts. That is a different envelope format focused on severity. Receipts and emit events are complementary streams, not the same stream in different wrappers.

Analyst review

When a SIEM rule fires or a session looks suspicious, an analyst pulls the full receipt stream for that session_id and reconstructs every decision in order. The hash chain confirms the stream has not been edited since it was written. The policy_hash confirms which policy was in force. The signature confirms the record came from Pipelock and not from a tampered agent log.

This is the audit-trail use case. Receipts are designed to be presentable to a third party (auditor, incident responder, internal review) without requiring trust in Pipelock itself. The signature and the chain are the trust anchors.

Long-window LLM detection

If detection involves feeding agent history to a reasoning model and asking “is this suspicious”, what you feed the model matters more than which model you pick. Raw log text lets an attacker shape the model’s view by shaping the agent’s reasoning tokens. Structured receipts do not.

A receipt stream is a deterministic sequence of fixed-field events. verdict, layer, pattern, policy_hash, and timestamp are not up for attacker manipulation. The attacker cannot inject text into the stream that changes what these fields say. The stream is signed and chain-linked, so injection is detectable.

That is the substrate a long-window LLM funnel runs on. Whether the funnel works is its own question. But if it does, it works on inputs the attacker cannot massage. Structured receipts are that kind of input. Raw reasoning tokens are not.

Verifying a stream

pipelock verify-receipt evidence/evidence-proxy-0.jsonl --key <public-key-hex>

Or with the Python verifier from PyPI:

pip install pipelock-verify
python -m pipelock_verify evidence/evidence-proxy-0.jsonl --key <public-key-hex>

Both exit 0 on success, 1 on any signature failure, chain break, or reordering. The two verifiers are byte-for-byte equivalent.

Consuming receipts in a detector

Once the stream verifies, a downstream detector reads it like any JSONL source.

import json
import subprocess
from typing import Callable, Iterator


def verified_receipts(path: str, pubkey_hex: str) -> Iterator[dict]:
    """Yield each receipt only if the full stream verifies."""
    check = subprocess.run(
        ["pipelock-verify", path, "--key", pubkey_hex],
        capture_output=True,
        text=True,
    )
    if check.returncode != 0:
        raise RuntimeError(check.stderr.strip() or check.stdout.strip())
    with open(path) as f:
        for line in f:
            if not line.strip():
                continue
            entry = json.loads(line)
            if entry.get("type") != "action_receipt":
                continue
            record = entry["detail"]["action_record"]
            record.setdefault("session_id", entry.get("session_id"))
            yield record


def route_to_siem(receipt: dict) -> None:
    if receipt["verdict"] == "block":
        forward_to_siem(receipt)
    if receipt["layer"] == "mcp_response_scan":
        score_for_llm_funnel(receipt)


for receipt in verified_receipts("evidence.jsonl", PUBKEY_HEX):
    route_to_siem(receipt)

The shape of the consumer is up to you. The shape of the input is fixed by the receipt spec.

What this does not solve

Signed receipts solve one narrow problem. They do not solve several others.

Compromised mediators can still lie. A receipt proves Pipelock recorded a decision. It does not prove Pipelock made the right decision. A signed wrong answer is still a wrong answer.

Real-time gateways still miss multi-week attacks. A slow-boiling attack where each step looks benign needs a long-window detector running on the receipt stream, not a faster gateway.

Receipts are input to detection, not a substitute for it. A stream of signed records does not tell you which sessions are compromised. Someone or something still has to look at the stream and make that call. Receipts give you a trustworthy place to look.

Agent-side attacks are out of scope. Pipelock sees what the agent tries to do on the network. If an attacker has already compromised the agent process (code execution, same-user file access, shared memory), receipts can document what the agent tried, but they cannot prevent the compromise.

Same-user deployments have a known ceiling. If Pipelock runs as the same Unix user as the agent, the agent can delete or truncate the receipt file. Capability separation has to come from the deployment layer (separate user, separate container).

See also

Frequently asked questions

Why does an agent firewall need a downstream detection layer?
Pipelock blocks what it can see on the wire. That is a hard, narrow job, and it is not the whole detection story. Long-running agents move through multi-week attack chains and produce actions that look benign in isolation. No real-time gateway will catch all of that. The gate is the first line, not the last. Signed receipts are the input the next line uses.
What is a signed action receipt?
A JSON record Pipelock emits for every proxy decision when receipt signing is enabled. Each receipt carries the verdict, layer, pattern, transport, session, principal, policy hash, side-effect classification, and a hash-chain link to the previous receipt. Each one is Ed25519-signed. Any deletion or reordering breaks the chain and the verifier rejects the stream.
How do I enable receipt signing?
Generate a key with pipelock keygen <name>, set flight_recorder.signing_key_path to the key path in your Pipelock config, then start or restart Pipelock. Receipts begin streaming to flight_recorder.dir immediately. The public key portion is what downstream verifiers need; the private key never leaves the host running Pipelock.
How is this different from logs?
Logs are unstructured text shaped by whoever wrote the log line. An attacker who can influence the agent reasoning tokens can shape the log narrative. Receipts are fixed-field, signed, and chain-linked. The fields a detector cares about, including verdict, layer, pattern, policy hash, and timestamp, are not up for attacker manipulation.
Which detection styles does this support?
Three. SIEM rules (Splunk, Datadog, Elastic, anything that ingests JSONL) for real-time alerting. Analyst review for incident response and audit, where the chain proves the stream is intact. Long-window LLM detection where receipts are the structured input for funnels of cheaper-then-stronger models reasoning over agent history.
How do I verify a stream?
Two reference verifiers ship: pipelock verify-receipt (Go, in the binary) and pipelock-verify (Python, on PyPI). Both exit 0 on success, 1 on any signature failure, chain break, or reordering. The two verifiers are byte-for-byte equivalent. The receipt format is open and other implementations can verify against the same vectors.
What does this not solve?
Compromised mediators can still lie: a signed wrong answer is still a wrong answer. Real-time gateways still miss multi-week attacks. Receipts are input to detection, not a substitute for it. Agent-side attacks (code execution on the agent process, shared memory) are out of scope. And same-user deployments have a known ceiling: an agent running as the same Unix user can truncate the receipt file. Capability separation has to come from the deployment layer.
Where is the schema?
The canonical receipt spec is at /learn/action-receipt-spec/ on this site. The Python reference verifier is at https://pypi.org/project/pipelock-verify/. Cross-implementation conformance vectors live in sdk/conformance/ in the Pipelock repo.

Ready to protect your own setup?