$ shellfirm

Audit Trail for Automation

Every automated command is logged with session ID, agent name, and full context

When AI agents and automation scripts run commands through shellfirm, every interaction is recorded in the audit log. This creates a complete trail of what was executed, what was blocked, and why.

What gets logged

Every command evaluated by shellfirm (whether via shell hook, MCP server, or CLI check) produces an audit event with:

FieldDescription
event_idUnique identifier for correlating pre/post challenge entries
timestampISO 8601 timestamp (UTC)
commandThe full command string
matched_idsList of matched pattern IDs (e.g., ["git:force_push"])
challenge_typeThe challenge type applied (Math, Enter, Yes, or Deny)
outcomeALLOWED, DENIED, SKIPPED, or CANCELLED
context_labelsRuntime context labels (e.g., ["branch=main", "ssh=true"])
severityHighest severity among matched checks
agent_nameName of the AI agent (if applicable)
agent_session_idSession ID of the agent connection (if applicable)
blast_radius_scopeImpact scope (RESOURCE, PROJECT, MACHINE)
blast_radius_detailHuman-readable impact description

Log format

Audit events are stored as JSON-lines (one JSON object per line) in ~/.shellfirm/audit.log:

{"event_id":"a1b2c3","timestamp":"2026-02-23T14:30:00Z","command":"git push --force origin main","matched_ids":["git:force_push"],"challenge_type":"Yes","outcome":"DENIED","context_labels":["branch=main"],"severity":"High","agent_name":"claude-code","agent_session_id":"sess-xyz-789","blast_radius_scope":"PROJECT","blast_radius_detail":"Overwrites remote branch history"}
{"event_id":"d4e5f6","timestamp":"2026-02-23T14:30:05Z","command":"git push --force-with-lease origin main","matched_ids":[],"challenge_type":"Math","outcome":"ALLOWED","context_labels":["branch=main"],"severity":"Medium","agent_name":"claude-code","agent_session_id":"sess-xyz-789"}

Agent-specific fields

When commands come through the MCP server, the audit log includes agent identification:

  • agent_session_id -- a unique ID generated per MCP server session. All commands from the same agent connection share this ID, making it easy to trace an agent's full command history.
  • agent_name -- identifies which agent originated the command (when provided by the client).

Querying the audit log

View recent events

shellfirm audit list

Search for specific patterns

# Find all denied commands
shellfirm audit search --outcome denied

# Find all commands from a specific agent session
shellfirm audit search --session sess-xyz-789

Using jq for analysis

Since the log is JSON-lines, you can use jq for powerful queries:

# All denied commands
cat ~/.shellfirm/audit.log | jq -c 'select(.outcome == "DENIED")'

# Commands from a specific agent session
cat ~/.shellfirm/audit.log | jq -c 'select(.agent_session_id == "sess-xyz-789")'

# High severity events in the last 24 hours
cat ~/.shellfirm/audit.log | jq -c 'select(.severity == "High" or .severity == "Critical")'

# Count outcomes by type
cat ~/.shellfirm/audit.log | jq -r '.outcome' | sort | uniq -c | sort -rn

# List all unique agent sessions
cat ~/.shellfirm/audit.log | jq -r '.agent_session_id // empty' | sort -u

Correlating events

Each command evaluation produces up to two audit events with the same event_id:

  1. Pre-challenge entry with outcome CANCELLED -- written before the challenge prompt. If the user hits Ctrl+C during the challenge, this is the only entry.
  2. Post-challenge entry with outcome ALLOWED, DENIED, or SKIPPED -- written after the challenge completes.

For agent/MCP commands (which are non-interactive), only the final outcome entry is written.

Integration with monitoring

Forward to SIEM

Tail the audit log and forward to your SIEM tool:

tail -f ~/.shellfirm/audit.log | your-siem-forwarder

Datadog / Splunk / ELK

Since the log is JSON-lines, it can be ingested directly by most log aggregation platforms:

# Filebeat configuration example
- type: log
  paths:
    - ~/.shellfirm/audit.log
  json.keys_under_root: true
  json.add_error_key: true

Alerting on denied commands

Set up alerts for patterns that indicate an agent misbehaving:

# Alert when the same agent session has more than 5 denials
cat ~/.shellfirm/audit.log \
  | jq -r 'select(.outcome == "DENIED") | .agent_session_id // empty' \
  | sort | uniq -c | sort -rn \
  | awk '$1 > 5 {print "ALERT: Agent session " $2 " has " $1 " denied commands"}'