OpenClaw Tool Call Audit Log: How to Capture Every Agent Action

← Back to Blog

Agentic Security

OpenClaw Tool Call Audit Log: How to Capture Every Agent Action

OpenClaw agents do real work: they run shell commands, read and write files, search memory, browse the web, and call external APIs. Every one of those actions is a tool call, and every tool call is a decision that can be audited, investigated, or replayed. The challenge is that OpenClaw's built-in session history was designed for conversation replay, not for a complete operational audit trail. Cron jobs, sub-agents, and isolated execution contexts slip through the cracks.

This article describes how to build a structured audit log that captures every tool invocation at the gateway level, regardless of which session or context triggered it. The approach uses OpenClaw's plugin hooks, a flat event schema, and local JSONL storage with optional cloud forwarding.

Why Tool Call Logging Matters

Tool calls are where agents interact with the real world. A model generating text is constrained to its context window; a model invoking exec or write can change files, delete data, make HTTP requests, and run arbitrary code. Three categories of stakeholders care about this:

  • Security teams need to know which commands ran, which files were accessed, and whether any policy violations occurred. Without a log, incident investigation after a misconfigured agent causes damage is guesswork.
  • Compliance officers need evidence that agent activity is traceable. Regulations like SOC 2, HIPAA, and GDPR require demonstrable audit trails for automated processes that touch sensitive data.
  • Engineering teams need debugging visibility. When a cron job fails silently or a sub-agent takes an unexpected action, the tool call sequence is the first thing to inspect.

OpenClaw's community has requested gateway-level audit logging specifically because the built-in session history misses cron jobs, sub-agents, and contexts where the logging agent is not the same as the executing agent. A plugin-based audit log closes that gap.

What OpenClaw Provides Today

OpenClaw has several mechanisms for viewing tool call history, but each has limitations for audit purposes:

Mechanism What it shows Audit gap
sessions.list Active and recent sessions with metadata No tool-level detail; session-scoped only
sessions_history Full conversation history including tool results Filters tool result content in list output; requires explicit session ID
sessions.tools Tools used within a specific session Session-scoped; no cross-session or cron-job aggregation
Diagnostics telemetry Model runs, message flow, queueing metrics Focuses on model calls, not individual tool invocations

The common thread: these are session-scoped views designed for conversation replay. They work well for inspecting a single chat thread, but they do not give you a flat, searchable, time-ordered stream of every tool call across all sessions, cron jobs, and sub-agents.

The Gateway Hook Approach

OpenClaw's plugin system exposes hooks that fire at specific points in the agent lifecycle. Two hooks are directly relevant to tool call auditing:

before_tool_call

Fires before every tool execution. Receives the tool name, arguments, and session context. This is your interception point: you can log what the agent intends to do, and optionally block the call if it violates a policy.

tool_result_persist

Fires after tool execution, when the result is being written to storage. Receives the tool name and output. This gives you the outcome: what the tool returned, whether it succeeded, and how much data came back.

By registering both hooks, you get a complete before-and-after record for every tool invocation. The key advantage over session-based history is that these hooks fire for every tool call in every context: interactive chats, cron-scheduled jobs, sub-agents spawned by other agents, and one-shot commands.

Hook registration is automatic. A plugin that exports a register(api) function can call api.on('before_tool_call', handler) and api.on('tool_result_persist', handler) during startup. The gateway invokes these handlers for every matching event, with no per-session configuration required.

What to Capture Per Tool Call

A useful audit event is structured, compact, and contains enough context to reconstruct what happened without storing the full tool output (which may contain sensitive data). Here is a practical schema:

Field Purpose Example
eventId Unique identifier (UUID v4) a1b2c3d4-e5f6-...
ts Epoch milliseconds 1710532800000
sessionId Session or cron-job identifier agent:main:cron:weekly-report
eventType Canonical event category tool_call, tool_result, policy_block
toolName Which tool was invoked exec, read, memory_search
toolPath Sanitized command or file path ls -la /home/user/docs
action What the audit layer decided allow, block, redact
policyHits Which rules matched (if any) ["shell.rm_rf_root"]
prevHash SHA-256 of the previous event (hash chain) 8f14e45f...

Notice what is deliberately excluded: the full tool output. Tool results often contain file contents, API responses, or query results that may include PII or proprietary data. The audit log captures the metadata of what happened (which tool, which arguments, which session, what decision), not the raw content. If you also need PII redaction on tool results, that is a separate layer that complements the audit log.

Structured Event Schema

A well-defined schema makes events queryable, filterable, and forward-compatible. Here is a v1 schema suitable for tool call auditing:

{ "v": 1, "ts": 1710532800000, "eventId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "prevHash": "8f14e45fceea167a5a36dedd4bea2543...", "sessionId": "agent:main:cron:weekly-report", "eventType": "tool_call", "toolName": "exec", "toolPath": "bash /scripts/generate-report.sh", "policyHits": [], "action": "allow", "redactionApplied": false }

Key design decisions:

  • Flat structure: no nested objects. Every event is a single JSON line that can be parsed, grepped, or loaded into a columnar store without transformation.
  • Hash chain: the prevHash field links each event to the previous one via SHA-256. This creates a tamper-evident chain: if any event is deleted or modified, the chain breaks. (For a deeper dive, see our guide on building immutable audit logs.)
  • Version field: the v field lets you evolve the schema without breaking consumers. Parsers check the version and apply the correct interpretation.

From Local Log to Searchable Dashboard

A JSONL file on the agent's machine is a good start, but it has limitations: you cannot search across multiple agents, you cannot set up alerts, and the file can be deleted (accidentally or intentionally). The next step is forwarding events to a central dashboard.

The forwarding pattern is straightforward:

  1. Buffer events in memory (batch of 50 or flush every 5 seconds, whichever comes first)
  2. POST the batch to an authenticated API endpoint over HTTPS
  3. Retry on failure with exponential backoff; events are not lost if the network is temporarily unavailable because they are already in the local JSONL
  4. Dashboard indexes events by session, tool, time, and action for filtering and search

The dashboard then gives you views that a flat file cannot: a timeline of all tool calls across all agents, filters by tool type or policy action, drill-down into specific sessions, and aggregations like "how many exec calls were blocked this week."

How Zedly Shield Fits

Building a tool call audit log from scratch requires writing the hook handlers, designing the schema, implementing the hash chain, setting up log rotation, building the forwarding pipeline, and creating a dashboard to view it all. Zedly Shield ships all of this as a single OpenClaw plugin.

  • One command to install: openclaw plugins install zedly-shield. The plugin registers before_tool_call, tool_result_persist, and four additional lifecycle hooks automatically.
  • Structured events from day one: every tool call is logged as a versioned ShieldEvent with tool name, sanitized path, session ID, policy decision, and hash chain linkage.
  • Cloud dashboard included: events forward to the Zedly Shield dashboard where you can filter by session, tool, action, and time range. The Runs tab groups events by session for a timeline view of each agent run.
  • Policy enforcement on the same hooks: the same before_tool_call hook that logs the event can also block dangerous commands (like rm -rf / or curl | bash) before they execute. Logging and enforcement share the same pipeline.

The local JSONL log continues to work even if the cloud dashboard is unreachable. Events are buffered and forwarded when connectivity returns. No tool call goes unrecorded.

Implementation Checklist

  1. Decide your hook scope. At minimum, register before_tool_call and tool_result_persist. For full lifecycle coverage, add message_received, before_agent_start, and agent_end.
  2. Define your event schema with a version field. Include tool name, sanitized arguments, session ID, timestamp, and action. Exclude raw tool output and PII.
  3. Implement hash chaining. Each event's prevHash should be the SHA-256 of the previous event's JSON. This makes tampering detectable.
  4. Write events to a local JSONL file in append-only mode. Use a dedicated directory with appropriate permissions.
  5. Implement batch forwarding to a central dashboard or SIEM. Buffer events and flush periodically; retry on failure.
  6. Sanitize tool arguments before logging. For exec, log the command string. For read/write, log the file path. Never log the full file content.
  7. Set up log rotation. Rotate JSONL files daily or when they exceed a size threshold. Retain rotated files according to your compliance policy.
  8. Test with cron jobs and sub-agents. Verify that tool calls from non-interactive contexts appear in the log. This is the gap that session-based history misses.
  9. Configure alerts for policy violations (blocked tool calls) and unusual patterns (unexpected tools, high call volume from a single session).

Run the OpenClaw Risk Check

See which tool calls in your OpenClaw deployment lack audit coverage. Our team will map your agent configuration, identify unmonitored tool paths, and recommend a logging strategy that covers interactive sessions, cron jobs, and sub-agents.

Explore Zedly Shield

Frequently Asked Questions

Does OpenClaw log tool calls by default?

OpenClaw records tool calls within its session history, and you can retrieve them through the sessions_history API or the sessions.tools command. However, this history is scoped to individual sessions and filters tool result content in list mode. Cron jobs, sub-agents, and isolated contexts may not appear in the same view. A gateway-level audit log captures every tool invocation regardless of session boundaries, giving you a single stream of all agent activity.

What is the difference between before_tool_call and tool_result_persist?

before_tool_call fires before the tool executes. It receives the tool name and arguments, and can block the call entirely by returning a cancel signal. tool_result_persist fires after execution, when the result is being written to storage. It receives the tool name and the output. Together they give you a complete before-and-after record: what was requested, and what was returned.

How much storage does a tool call audit log use?

Each event is a single JSON line, typically 200 to 500 bytes. A busy agent running 50 tool calls per hour generates roughly 600 KB per day of JSONL. At that rate, a year of local logs fits comfortably in under 250 MB. If you forward events to a cloud dashboard, the local log can be rotated on a shorter cycle.

Can I audit tool calls without modifying my OpenClaw configuration?

You need to install a plugin that registers the relevant hooks. With Zedly Shield, this is a single command (openclaw plugins install zedly-shield) and a small config block. The plugin registers hooks automatically during gateway startup. No changes to your agent code, tools, or prompts are required.

Does auditing tool calls slow down my agent?

The overhead is minimal. Writing a JSON line to a local file takes under a millisecond. Forwarding events to a remote dashboard happens asynchronously in batches, so it does not block the tool execution path. The gateway hook itself executes synchronously, but the work inside (JSON serialization, file append) is fast enough that agents do not notice the difference.

Ready to get started?

Runtime safety for agentic AI. PII redaction, policy-based blocking, and tamper-evident audit logs for OpenClaw.