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
curlorwgetto 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:
- Rotate the compromised credentials immediately (assume compromise if secrets leaked)
- Check if keys were sent to external services
- 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
loopbackonly) - File permissions on
~/.openclawdirectory - 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
execcommands were expected - No file writes to sensitive directories
- Gateway is bound to
loopbackonly - 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
exectool is the highest-risk surface to monitor - Built-in
openclaw security auditcatches 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:
- Was there a user message requesting this?
- Did the agent misinterpret instructions?
- 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 datetimemessage.role: "user" | "assistant" | "toolResult"message.content[]: Array of text, thinking, or tool callsmessage.usage.cost.total: API cost per response
Tested on OpenClaw 1.x, Node.js 22.x, macOS Sonoma & Ubuntu 24.04