Documentation Index
Fetch the complete documentation index at: https://docs.rafter.so/llms.txt
Use this file to discover all available pages before exploring further.
Audit Log
Rafter writes a security audit log to ~/.rafter/audit.jsonl in JSONL format (newline-delimited JSON). Every security-relevant action — policy enforcement decisions, secret detections, 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
The directory ~/.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" |
Example:
{
"timestamp": "2026-02-20T10:30:45.123Z",
"sessionId": "1740047445123-k8f2m",
"eventType": "command_intercepted",
"agentType": "claude-code",
"action": {
"command": "git push --force",
"riskLevel": "high"
},
"securityCheck": {
"passed": false,
"reason": "High-risk command requires approval"
},
"resolution": {
"actionTaken": "blocked"
}
}
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" |
The audit log never contains the raw secret value—only the type (e.g., “AWS Access Key”) and location (e.g., “staged files”, “config.js”).
Example:
{
"timestamp": "2026-02-20T10:25:12.456Z",
"sessionId": "1740047445123-k8f2m",
"eventType": "secret_detected",
"agentType": "openclaw",
"action": {
"riskLevel": "critical"
},
"securityCheck": {
"passed": false,
"reason": "AWS Access Key detected in config.js"
},
"resolution": {
"actionTaken": "blocked"
}
}
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" |
Example:
{
"timestamp": "2026-02-20T11:00:00.000Z",
"sessionId": "1740047445123-k8f2m",
"eventType": "content_sanitized",
"securityCheck": {
"passed": false,
"reason": "3 sensitive patterns detected",
"details": {
"contentType": "shell_output",
"patternsMatched": 3
}
},
"resolution": {
"actionTaken": "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_detected events record the secret type and file location, not the secret itself.
- Content is not stored.
content_sanitized events record pattern counts and content types, not the raw content.
- Commands are logged verbatim.
command_intercepted (policy enforcement) events 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
retentionDays are purged when cleanup() 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. |
Config file example:
{
"agent": {
"audit": {
"logAllActions": true,
"retentionDays": 90,
"logLevel": "info"
}
}
}
Policy file example (.rafter.yml):
audit:
retention_days: 90
log_level: info
Webhook Notifications
When configured, the audit logger sends a POST request to a webhook URL for events at or above a minimum risk level. Works with Slack incoming webhooks, Discord webhooks, and generic HTTP endpoints.
Configuration
| Key | Type | Default | Description |
|---|
agent.notifications.webhook | string | — | Webhook URL to POST notifications to |
agent.notifications.minRiskLevel | string | "high" | Minimum risk level to trigger notification ("high" or "critical") |
Webhook Payload
{
"event": "command_intercepted",
"risk": "high",
"command": "git push --force",
"timestamp": "2026-02-21T10:30:45.123Z",
"agent": "claude-code",
"text": "[rafter] high-risk event: command_intercepted — git push --force",
"content": "[rafter] high-risk event: command_intercepted — git push --force"
}
The text field provides Slack compatibility. The content field provides Discord compatibility. Both contain a human-readable summary.
Webhook delivery is fire-and-forget with a 5-second timeout. Failures are silently ignored to avoid disrupting audit logging.
Setup
# Configure webhook URL
rafter agent config set agent.notifications.webhook https://hooks.slack.com/services/T.../B.../xxx
# Only notify on critical events
rafter agent config set agent.notifications.minRiskLevel critical
# Disable notifications
rafter agent config set agent.notifications.webhook ""
Querying the Audit Log
Use rafter agent audit to view and filter entries:
# Show last 10 entries (default)
rafter agent audit
# Show last 50 entries
rafter agent audit --last 50
# Filter by event type
rafter agent audit --event secret_detected
# Filter by agent
rafter agent audit --agent claude-code
# Entries since a specific date
rafter agent audit --since 2026-02-01
# Combine filters
rafter agent audit --event command_intercepted --agent openclaw --last 100
Or query the JSONL file directly with standard tools:
# Count blocked commands
grep '"actionTaken":"blocked"' ~/.rafter/audit.jsonl | wc -l
# Find all secret detections with jq
jq 'select(.eventType == "secret_detected")' ~/.rafter/audit.jsonl
# Events from the last 24 hours
jq --arg cutoff "$(date -u -v-1d +%Y-%m-%dT%H:%M:%S)" \
'select(.timestamp > $cutoff)' ~/.rafter/audit.jsonl
MCP Access
The read_audit_log MCP tool exposes audit log entries to MCP clients:
{
"tool": "read_audit_log",
"arguments": {
"event_type": "secret_detected",
"limit": 20
}
}
See MCP Integration for setup.