Audit Log
Rafter writes a security audit log to~/.rafter/audit.jsonl in JSONL format (newline-delimited JSON). Every security-relevant action—command interceptions, secret detections, policy overrides—is recorded as a single JSON object per line.
Both the Node and Python CLIs write to the same file using the same schema. The Node CLI is the reference implementation; the Python CLI currently emits command_intercepted and secret_detected events only.
File Location
~/.rafter/ is created automatically on first use.
Schema
Every audit log entry contains these fields:Base Fields
| Field | Type | Required | Description |
|---|---|---|---|
timestamp | string | yes | ISO 8601 UTC timestamp (e.g., 2026-02-20T10:30:45.123Z) |
sessionId | string | yes | Unique per CLI invocation. Format: {epoch_ms}-{random} |
eventType | string | yes | Event type identifier (see Event Types) |
agentType | string | no | AI agent platform: "openclaw" or "claude-code" |
action Object
Present on most events. Contains context about what triggered the event.
| Field | Type | Required | Description |
|---|---|---|---|
command | string | no | Shell command string |
tool | string | no | Tool name (e.g., Write, Bash) |
riskLevel | string | no | "low", "medium", "high", or "critical" |
securityCheck Object
Always present. Records the outcome of the security evaluation.
| Field | Type | Required | Description |
|---|---|---|---|
passed | boolean | yes | Whether the security check passed |
reason | string | no | Human-readable explanation |
details | object | no | Structured metadata (event-specific) |
resolution Object
Always present. Records what action was taken.
| Field | Type | Required | Description |
|---|---|---|---|
actionTaken | string | yes | "blocked", "allowed", "overridden", or "redacted" |
overrideReason | string | no | User-provided justification (only on policy_override) |
Event Types
command_intercepted
Emitted when a shell command is evaluated against the security policy.
| Field | Value |
|---|---|
action.command | The shell command string |
action.riskLevel | Dynamically assessed: low, medium, high, or critical |
securityCheck.passed | true if allowed, false if blocked |
securityCheck.reason | Why the command was blocked/flagged |
resolution.actionTaken | "allowed", "blocked", or "overridden" |
secret_detected
Emitted when a secret is found in files, staged content, or tool output.
| Field | Value |
|---|---|
action.riskLevel | Always "critical" |
securityCheck.passed | Always false |
securityCheck.reason | "{secret_type} detected in {location}" |
resolution.actionTaken | "blocked" or "allowed" |
content_sanitized
Emitted when sensitive patterns are redacted from output.
| Field | Value |
|---|---|
securityCheck.passed | Always false |
securityCheck.reason | "{n} sensitive patterns detected" |
securityCheck.details | { "contentType": "...", "patternsMatched": n } |
resolution.actionTaken | Always "redacted" |
policy_override
Emitted when a user explicitly overrides a security policy (e.g., --force flag).
| Field | Value |
|---|---|
action.command | The overridden command (optional) |
action.riskLevel | Always "high" |
securityCheck.passed | Always false |
securityCheck.reason | "Security policy overridden by user" |
resolution.actionTaken | Always "overridden" |
resolution.overrideReason | User-provided reason string |
scan_executed
Reserved for future use. Will be emitted when file scans are performed.
config_changed
Reserved for future use. Will be emitted when security configuration is modified.
Redaction Behavior
The audit log is designed to be safe to retain and share:- Secret values are never logged.
secret_detectedevents record the secret type and file location, not the secret itself. - Content is not stored.
content_sanitizedevents record pattern counts and content types, not the raw content. - Commands are logged verbatim.
command_interceptedevents include the full command string. If commands contain sensitive arguments, they appear in the log.
Size and Rotation
- No automatic rotation. The log file grows unbounded until cleanup runs.
- Time-based retention: Entries older than
retentionDaysare purged whencleanup()is called. - No automatic scheduling. Cleanup must be triggered manually or via the API.
- Default retention: 30 days.
Configuration
Configure audit logging in~/.rafter/config.json under agent.audit, or in .rafter.yml under audit:
| Key | Type | Default | Description |
|---|---|---|---|
logAllActions | boolean | true | Master switch. If false, no events are written. |
retentionDays | number | 30 | Days to retain entries before cleanup purges them. |
logLevel | string | "info" | Stored in config but not currently used for filtering. |
.rafter.yml):
Querying the Audit Log
Userafter agent audit to view and filter entries:
MCP Access
Theread_audit_log MCP tool exposes audit log entries to MCP clients:

