Audit OpenClaw Logs in 12 Minutes: Track Every AI Agent Action

Find what your OpenClaw agent did, when it did it, and why. Parse JSONL transcripts, extract tool calls, and catch security issues before they escalate.

Problem: Your AI Agent Did Something — But What?

Your OpenClaw agent just sent 47 emails, ran shell commands, and modified files. You need to know exactly what happened, verify nothing leaked secrets, and ensure compliance with your security policy.

You'll learn:

  • Where OpenClaw stores audit logs and what they contain
  • How to parse JSONL transcripts to extract tool calls
  • Security checks to run after suspicious agent behavior

Time: 12 min | Level: Intermediate


Why This Matters

OpenClaw stores session transcripts on disk under ~/.openclaw/agents/<agentId>/sessions/*.jsonl. This is required for session continuity and session memory indexing, but it also means any process with filesystem access can read those logs.

Common scenarios:

  • Agent executed commands you didn't explicitly request
  • Need audit trail for compliance or security review
  • Debugging why agent made specific decisions
  • Verifying no API keys were exposed in logs

Solution

Step 1: Locate Your Session Transcripts

# Find your agent ID from the system prompt or config
cat ~/.openclaw/openclaw.json | grep -A 5 "agents"

# List all session logs for an agent
ls -lh ~/.openclaw/agents/<agentId>/sessions/

Expected: You'll see:

  • sessions.json - Index mapping session keys to IDs
  • <session-id>.jsonl - One file per conversation session

File structure:

  • Each line is a JSON object (JSONL format)
  • Contains user messages, assistant responses, and tool calls
  • Append-only log with timestamps

Step 2: Parse JSONL Logs with jq

Install jq if you don't have it:

# macOS
brew install jq

# Ubuntu/Debian
sudo apt-get install jq

# Verify installation
jq --version

List all sessions by date and size:

for f in ~/.openclaw/agents/<agentId>/sessions/*.jsonl; do
  date=$(head -1 "$f" | jq -r '.timestamp' | cut -dT -f1)
  size=$(ls -lh "$f" | awk '{print $5}')
  echo "$date $size $(basename $f)"
done | sort -r

Expected output:

2026-02-07 2.3M abc123.jsonl
2026-02-06 156K def456.jsonl
2026-02-05 89K ghi789.jsonl

Step 3: Extract Tool Calls

See which tools were used:

jq -r '.message.content[]? | select(.type == "toolCall") | .name' \
  ~/.openclaw/agents/<agentId>/sessions/<session-id>.jsonl \
  | sort | uniq -c | sort -rn

Example output:

     47 web_search
     23 exec
     12 write
      5 browser_action

Why this matters: The exec tool runs shell commands. If you see unexpected exec calls, investigate immediately.


Step 4: Audit Specific Commands

Extract all shell commands executed:

jq -r 'select(.message.content[]?.type == "toolCall" and .message.content[]?.name == "exec") 
  | .message.content[] | select(.name == "exec") | .input.command' \
  ~/.openclaw/agents/<agentId>/sessions/<session-id>.jsonl

Expected: List of every shell command the agent ran.

Red flags to check:

  • Commands with curl or wget to unknown domains
  • File operations on sensitive directories (/etc, ~/.ssh)
  • Commands containing API keys or tokens

Step 5: Search for Sensitive Data

Check if API keys appeared in conversation:

# Search for common API key patterns
rg -i 'sk-[a-zA-Z0-9]{32,}|AIza[a-zA-Z0-9]{35}' \
  ~/.openclaw/agents/<agentId>/sessions/*.jsonl

If keys are found:

  1. Rotate the compromised credentials immediately (assume compromise if secrets leaked)
  2. Check if keys were sent to external services
  3. Review tool call logs for unauthorized API usage

Step 6: Run Security Audit

OpenClaw has built-in security scanning:

# Quick audit
openclaw security audit

# Deep scan (checks permissions, exposed services)
openclaw security audit --deep

# Auto-fix common issues
openclaw security audit --fix

What it checks:

  • Gateway exposure (should be loopback only)
  • File permissions on ~/.openclaw directory
  • DM policies (should require pairing)
  • Dangerous tool allowlists

If it fails:

  • Gateway exposed on LAN: Set gateway.bind: "loopback" in config
  • World-readable logs: Run chmod 700 ~/.openclaw
  • Open DM policy: Change to "dmPolicy": "pairing"

Advanced: Extract Full Conversation

Get human-readable transcript:

# Extract user messages
jq -r 'select(.message.role == "user") 
  | .message.content[]? | select(.type == "text") | .text' \
  <session-id>.jsonl

# Extract assistant responses
jq -r 'select(.message.role == "assistant") 
  | .message.content[]? | select(.type == "text") | .text' \
  <session-id>.jsonl

Calculate session cost:

jq -s '[.[] | .message.usage.cost.total // 0] | add' <session-id>.jsonl

Verification

Confirm logs are readable:

# Should return valid JSON
head -1 ~/.openclaw/agents/<agentId>/sessions/<session-id>.jsonl | jq .

You should see: Structured JSON with timestamp, message, and sessionKey fields.

If parsing fails:

  • Error: "parse error": File may be corrupted or still being written
  • No output: Check you're using correct <agentId> and <session-id>

Security Checklist

After auditing, verify:

  • No API keys in plaintext logs
  • All exec commands were expected
  • No file writes to sensitive directories
  • Gateway is bound to loopback only
  • DM policy requires pairing
  • Implement retention policy — prune old transcripts containing sensitive data

Set log retention:

# Delete logs older than 30 days
find ~/.openclaw/agents/*/sessions/*.jsonl -mtime +30 -delete

What You Learned

  • JSONL transcripts provide a factual, line-by-line audit of what happened (user messages, tool calls, execution results)
  • OpenClaw logs are NOT redacted by default - treat them as sensitive
  • The exec tool is the highest-risk surface to monitor
  • Built-in openclaw security audit catches common misconfigurations

Limitation: Console redaction doesn't affect file logs. Even with logging.redactSensitive: "tools", secrets may still be in JSONL files.


Real-World Example

Scenario: Agent sent emails you didn't authorize.

# Find when emails were sent
jq -r 'select(.message.content[]?.type == "toolCall" 
  and (.message.content[]?.name | contains("email"))) 
  | .timestamp + " " + (.message.content[] | select(.name) | .name)' \
  ~/.openclaw/agents/main/sessions/*.jsonl

What to check:

  1. Was there a user message requesting this?
  2. Did the agent misinterpret instructions?
  3. Was this a prompt injection via web search results?

Action: Review user messages immediately before tool calls to trace causality.


Quick Reference

Log locations:

  • Gateway logs: /tmp/openclaw/openclaw-YYYY-MM-DD.log
  • Session transcripts: ~/.openclaw/agents/<agentId>/sessions/*.jsonl
  • Config: ~/.openclaw/openclaw.json

Essential commands:

# List sessions
ls ~/.openclaw/agents/*/sessions/

# Extract tool names
jq -r '.message.content[]? | select(.type=="toolCall") | .name' <file>.jsonl

# Search for keywords
rg 'keyword' ~/.openclaw/agents/*/sessions/*.jsonl

# Security audit
openclaw security audit --deep

Log fields:

  • timestamp: ISO 8601 datetime
  • message.role: "user" | "assistant" | "toolResult"
  • message.content[]: Array of text, thinking, or tool calls
  • message.usage.cost.total: API cost per response

Tested on OpenClaw 1.x, Node.js 22.x, macOS Sonoma & Ubuntu 24.04