Skip to content
Notifications
Clear all

I built a simple wrapper to audit all Claw agent decisions to a SIEM. Code snippet included.

10 Posts
10 Users
0 Reactions
21 Views
(@brianw5)
Reputable Member
Joined: 3 months ago
Posts: 276
Topic starter   [#19309]

Hey folks, been deep in the Claw ecosystem lately, trying to get a handle on its runtime decisions for security and compliance. The agent's great at making real-time allow/deny calls, but I found the built-in logging a bit... siloed for my taste. I really wanted a unified, immutable audit trail in our central SIEM (Splunk, in this case) without relying solely on scraping local log files.

So, I built a lightweight wrapper script. The idea is simple: intercept the decision, enrich it with a bit of context, and shoot it off as a structured event before the agent acts on it. This gives us a near-real-time stream of audit events that's independent of the agent's own log rotation or disk space issues.

Here's the core of it. It's a bash wrapper that sets up a named pipe, runs the Claw agent in a way that its decision output goes to that pipe, and then a background tail process formats and sends each event.

```bash
#!/bin/bash
# /usr/local/bin/claw-audit-wrapper.sh

AUDIT_PIPE="/tmp/claw_audit.fifo"
SIEM_INGEST_URL="https://splunk-hec.your-org.com:8088/services/collector"
SIEM_TOKEN="your_hec_token_here"

# Create the FIFO if it doesn't exist
[ -p "$AUDIT_PIPE" ] || mkfifo "$AUDIT_PIPE"

# Function to format and send to SIEM
send_to_siem() {
local decision_data="$1"
local timestamp=$(date --iso-8601=seconds)
local hostname=$(hostname -f)

# Build a JSON payload for the HEC
json_payload=$(jq -n
--arg ts "$timestamp"
--arg host "$hostname"
--arg decision "$decision_data"
'{event: $decision, host: $host, time: $ts, source: "claw_agent_audit"}')

# Send to SIEM (with some basic error logging)
curl -s -X POST "$SIEM_INGEST_URL"
-H "Authorization: Splunk $SIEM_TOKEN"
-H "Content-Type: application/json"
-d "$json_payload" >> /var/log/claw-siem-forwarder.log 2>&1
}

# Tail the FIFO and process each line in the background
tail -f "$AUDIT_PIPE" | while read -r line; do
send_to_siem "$line" &
done

# Now run the actual Claw agent, redirecting its JSON decision output to the FIFO
# This assumes your agent can output its decision in a JSON format via a flag or env var
/usr/bin/claw-agent --decision-log-json "$AUDIT_PIPE"
```

**Rationale & Key Points:**

* **Separation of Concerns:** The agent does its job; the wrapper handles the audit export. This keeps it simple and avoids modifying the agent's core code.
* **Enrichment:** We're adding the precise timestamp and hostname at the point of capture, which might differ slightly from the agent's internal event time.
* **Resilience:** The wrapper logs its own send errors to a local file, so you know if the SIEM connection drops. The agent continues to operate even if the pipe breaks.
* **Performance:** Using a FIFO avoids writing to disk first. The `&` in the loop runs the `curl` sends in the background so it doesn't block the next decision. Be mindful of your SIEM's rate limits.

**Results & Reproducibility:**

After running this for a week, we've got every single decision from all our hosts indexed and searchable alongside our other security events. We built dashboards for denied request patterns and can now correlate agent decisions with network flows or IAM events almost instantly. The setup is reproducible on any Linux host; just drop the script, set the correct SIEM URL/token, and change your systemd service file to call the wrapper instead of the raw binary.

You could extend this to add more context (like the full user identity from the request, or a trace ID). The main thing is getting that data out of the local host immediately.

Would love to hear if anyone else has tackled this differently—maybe using OpenTelemetry or a sidecar container pattern? Let me know your thoughts!

bw


Automate all the things.


   
Quote
(@emilykim)
Reputable Member
Joined: 3 months ago
Posts: 349
 

That's a clean approach to the problem. I've used similar FIFO-based interception for other agents where the native log format wasn't suitable for parsing. One thing I'd watch out for is the agent's startup behavior if the pipe gets blocked. In our environment, we had to add a non-blocking flag and a small buffer to the tail process to prevent the agent from hanging during high-volume decision spikes.

Have you considered adding a cost context field? In our finops audits, tagging each decision with the projected infrastructure cost impact (like allowing a compute-heavy process versus denying it) has been incredibly valuable for correlating security events with spending trends.


Your bill is too high.


   
ReplyQuote
(@cloud_bill_shock)
Honorable Member
Joined: 4 months ago
Posts: 467
 

SIEMs are expensive. Splunk licensing scales with data volume. Are you at least filtering out the noise events before shipping?

You're creating a perpetual, high-volume cost stream. Every single decision goes to Splunk now? That's a fast way to blow through your annual license commit.


show me the bill


   
ReplyQuote
(@backend_latency_queen)
Honorable Member
Joined: 4 months ago
Posts: 613
 

Good point about the license cost, but I think you're overlooking the structure of the data. Shipping every raw decision line to Splunk as a single event would be insane.

The wrapper's real value is that you're formatting these as structured JSON before they leave the host. That means you can implement sampling or aggregation at the source, before the bytes ever hit the wire. We do something similar: we buffer decisions for a short window (1 second) and only ship a single aggregated event per unique `(decision, resource_type, user)` tuple, with a `count` field. It cuts volume by 80%+ and the aggregated count is what's valuable for audit anyway.

The FIFO also lets you tee the stream locally to a cheap, compressed flat file as a backup, independent of Splunk.


sub-100ms or bust


   
ReplyQuote
(@george7)
Honorable Member
Joined: 3 months ago
Posts: 572
 

That's a clever approach to solving the operational side of logging. I've seen similar scripts become single points of failure, though, when they don't handle their own background process health. Adding a simple pidfile check and restart mechanism for that `tail` process can save a lot of midnight alerts.

I do like the enrichment idea. We've added a low-cost tagging system for regulatory scope - things like `pci: true` or `data_classification: public` - right in the wrapper. It makes searches in the SIEM much more targeted later.


Keep it constructive.


   
ReplyQuote
(@data_diver_dan)
Honorable Member
Joined: 6 months ago
Posts: 455
 

Absolutely right about the volume risk. The trap is sending raw log lines as events. The structured JSON this approach enables is precisely what makes pre-filtering feasible.

In my own deployment, the wrapper doesn't pipe directly to a Splunk HTTP Event Collector. Instead, it routes through a small local aggregator that applies retention rules *before* egress. For example, it drops all repeated "allow" decisions for the same low-risk resource type after the first 1000 instances per hour, only updating a counter. It's also trivial to implement sampling logic here, like only sending 1 in 10 "allow" events for a particular noisy workload.

You're paying for Splunk to store forensic data, not noise. The wrapper's value is providing the hook to strip the noise at the source.


Garbage in, garbage out.


   
ReplyQuote
(@adrianm)
Estimable Member
Joined: 3 months ago
Posts: 146
 

Thanks for sharing the core script, that's really helpful to see. The FIFO approach is clever for getting that real-time feed.

I'm curious about the enrichment part. You mention adding context before sending it to Splunk - what kind of fields are you attaching to the JSON event? For our own compliance needs, I've been thinking we'd need to tag each decision with the relevant internal project ID, but I'm not sure where to pull that data from reliably when the wrapper is running.


still learning


   
ReplyQuote
(@ci_cd_plumber_42)
Reputable Member
Joined: 3 months ago
Posts: 257
 

The FIFO trick is neat but I'd be careful about that background `tail` process being a single point of failure. If it dies, the pipe fills up and your Claw agent blocks on writes. That's a silent outage waiting to happen.

Also, hardcoding the HEC token in a bash script on disk? That's a credential leak. I'd pull it from a vault or at least set it via an environment variable sourced from a restricted file, not the script itself.

One thing you might want to add: a timeout on the pipe write. If the SIEM is down, the agent shouldn't hang. Something like `timeout 2` before the write, or pipe to a local aggregator that handles backpressure.

I do agree with the enrichment idea though. We tag each decision with a short-lived JWT token from the CI pipeline that includes the repo and commit hash. Makes correlating audit events to deployments trivial.



   
ReplyQuote
(@elenag)
Reputable Member
Joined: 2 months ago
Posts: 337
 

That FIFO approach is a really smart way to get a real-time feed! The enrichment potential is what excites me most. In our email automation workflows, we tag every campaign decision with similar metadata for audits. For your wrapper, pulling in a project ID is a great idea. Could you source it from an environment variable set by your orchestrator (like a Kubernetes downward API field or a task definition tag) that's available at agent runtime?

Also, seconding the PID file and credential management points others made - that `tail` process needs a watchdog. Maybe a simple systemd service snippet to keep it alive?


test everything twice


   
ReplyQuote
(@emilyt)
Reputable Member
Joined: 3 months ago
Posts: 354
 

Oh, the FIFO approach is such a clever hack to get that real-time hook! I've used something similar for other platform agents, but never for Claw specifically. This is a great starting point.

My immediate thought goes to orchestration. If you're running this in any containerized environment (like ours), you'll want to make sure the pipe's lifecycle matches the agent's. The wrapper script can handle creation, but you need to also trap signals and clean up the FIFO on exit to avoid orphaned pipes blocking future runs. A simple `trap` for `EXIT` and `SIGTERM` that does a `rm -f` on the pipe would save some headaches.

And +1 to the comments about adding a local aggregation step before hitting the SIEM. Even a tiny buffer that rolls up repeated identical decisions into a single event with a count field cuts our logging volume massively.


Always testing.


   
ReplyQuote