Audit Log
TameFlare maintains an append-only audit log that records every action request, policy decision, approval, execution, and system event. This page covers the audit architecture, schema, retention, export, and compliance evidence.
Architecture
Append-only design
The audit log is append-only. There is no API to delete, modify, or overwrite audit events. Every event is written once and never changed.
| Property | Status |
|---|---|
| Append-only writes | Yes — INSERT only, no UPDATE or DELETE |
| Delete API | None — no endpoint exists to delete audit events |
| Modification API | None — events are immutable after creation |
| Cryptographic chaining | Not implemented — events are not hash-chained. A database admin with direct SQLite access could theoretically modify or delete rows. |
| Timestamps | Server-side UTC — set by the control plane, not the agent |
| Ordering | Monotonic — events are inserted in order with auto-incrementing IDs |
What gets logged
Every significant action in TameFlare creates an audit event:
| Trigger | Event type | Logged by |
|---|---|---|
| Agent requests an action | action.requested | Control plane |
| Policy evaluates an action | action.allowed, action.denied | Control plane |
| Action requires approval | action.pending_approval | Control plane |
| Human approves/denies | approval.responded | Control plane |
| Action is executed via gateway | action.executed | Control plane |
| Kill switch toggled | kill_switch.activated, kill_switch.deactivated | Control plane |
| Agent registered/suspended/revoked | agent.registered, agent.suspended, agent.revoked | Control plane |
| Policy created/updated/deleted | policy.created, policy.updated, policy.deleted | Control plane |
| Gateway created/updated | gateway.created, gateway.updated | Control plane |
| User login/logout | user.login, user.logout | Control plane |
| Config export/import | config.exported, config.imported | Control plane |
Event schema
Every audit event contains these fields:
| Field | Type | Example | Description |
|---|---|---|---|
| id | string | evt_abc123 | Unique event ID |
| event_type | string | action.denied | Event type (see list above) |
| org_id | string | org_xyz | Organization ID |
| agent_id | string | agent_deploy_bot | Agent that triggered the event (if applicable) |
| user_id | string | user_abc | User that triggered the event (if applicable) |
| action_request_id | string | act_def456 | Related action request (if applicable) |
| details | JSON | {"decision": "deny", ...} | Event-specific payload |
| created_at | datetime | 2026-02-08T20:00:00Z | UTC timestamp |
Event-specific details
action.requested
{
"action_type": "github.pr.merge",
"resource": {"provider": "github", "target": "acme/api", "environment": "production"},
"parameters": {"pull_number": 42},
"risk_score": 45,
"risk_hints": {"production_target": true, "irreversible": false}
}action.denied
{
"decision": "deny",
"reason": "Cannot delete protected branches",
"matched_policy": "pol_github_branch_safety",
"matched_rule": "block-protected-branch-deletion",
"risk_score": 85
}action.allowed
{
"decision": "allow",
"reason": "Development actions are auto-approved",
"matched_policy": "pol_github_branch_safety",
"matched_rule": "allow-dev-actions",
"decision_token_kid": "configured_key",
"risk_score": 10
}approval.responded
{
"approved": true,
"responded_by": "alice@company.com",
"response_time_seconds": 45,
"note": "Reviewed PR, looks good"
}kill_switch.activated
{
"scope": "all",
"activated_by": "alice@company.com",
"reason": "Suspicious agent behavior detected"
}Complete event type list
| Event type | Category | Description |
|---|---|---|
| action.requested | Actions | Agent submitted an action request |
| action.allowed | Actions | Policy evaluation returned allow |
| action.denied | Actions | Policy evaluation returned deny |
| action.pending_approval | Actions | Action requires human approval |
| action.executed | Actions | Action was executed via gateway |
| action.execution_failed | Actions | Gateway execution failed |
| approval.responded | Approvals | Human approved or denied a pending action |
| approval.expired | Approvals | Approval timed out (5 min) |
| kill_switch.activated | Kill switch | Kill switch turned on |
| kill_switch.deactivated | Kill switch | Kill switch turned off |
| agent.registered | Agents | New agent created |
| agent.suspended | Agents | Agent suspended |
| agent.activated | Agents | Agent reactivated |
| agent.revoked | Agents | Agent permanently revoked |
| policy.created | Policies | New policy created |
| policy.updated | Policies | Policy definition or metadata updated |
| policy.deleted | Policies | Policy deleted |
| policy.enabled | Policies | Policy enabled |
| policy.disabled | Policies | Policy disabled |
| gateway.created | Gateways | New gateway configured |
| gateway.updated | Gateways | Gateway settings updated |
| user.login | Auth | User logged into dashboard |
| user.logout | Auth | User logged out |
| user.registered | Auth | New user account created |
| config.exported | Config | Configuration exported |
| config.imported | Config | Configuration imported |
Retention
Per-tier retention
| Tier | Default retention | Configurable? |
|---|---|---|
| Starter (free) | 30 days | Yes, via AUDIT_RETENTION_DAYS |
| Pro ($20/mo) | 30 days | Yes |
| Team ($49/mo) | 1 year | Yes |
| Enterprise | Custom | Yes |
Cleanup job
Audit events older than the retention period are purged by the maintenance cleanup job:
# Manual cleanup
curl -X POST \
-H "Authorization: Bearer $MAINTENANCE_SECRET" \
http://localhost:3000/api/maintenance/cleanupThe cleanup job purges:
- Audit events older than
AUDIT_RETENTION_DAYS - Expired session tokens
- Used nonces older than 24 hours
Automated cleanup (cron)
# Daily at 3 AM
0 3 * * * curl -sf -X POST -H "Authorization: Bearer $MAINTENANCE_SECRET" http://localhost:3000/api/maintenance/cleanupWhat happens after retention
- Events are permanently deleted from the SQLite database
- No archive is created automatically — export before retention expires if you need long-term storage
- The cleanup is irreversible
Export
CSV export (dashboard)
- Navigate to Audit Log in the dashboard
- Apply filters (event type, date range, agent)
- Click Export CSV
The CSV includes all fields: event ID, type, agent, user, timestamp, and the full details JSON.
CSV export (API)
curl -H "Cookie: session=YOUR_SESSION" \
"http://localhost:3000/api/dashboard/audit-export?event_type=action.denied&from=2026-01-01&to=2026-02-01" \
> denied-actions-jan.csvQuery parameters:
event_type— filter by event typefrom/to— date range (ISO 8601)agent_id— filter by agent
Webhook forwarding
Configure webhook_url on action requests to receive real-time event notifications:
{
"action_spec": { "type": "github.pr.merge", ... },
"webhook_url": "https://your-siem.example.com/ingest/TameFlare"
}TameFlare sends a POST to the webhook URL when the action is decided (allowed, denied, or approved). The payload includes the full action spec, decision, and audit metadata.
SIEM integration
| SIEM | Integration method | |---|---| | Splunk | Webhook forwarding to Splunk HEC endpoint | | Datadog | Webhook forwarding to Datadog Log API, or CSV export + Datadog Agent | | Elastic/ELK | Webhook forwarding to Logstash HTTP input, or CSV import | | Sumo Logic | Webhook forwarding to HTTP Source | | Custom | Webhook to any HTTP endpoint, or periodic CSV export via cron |
There is no native syslog output. For syslog integration, use a webhook-to-syslog bridge or export CSV and ingest via your SIEM's file collector.
Compliance evidence
Proving the approval chain
For auditors who need to verify that an action was properly authorized, TameFlare provides a complete chain of evidence:
1. action.requested — Agent submitted the request (timestamp, action spec, risk score)
2. action.denied OR action.pending_approval — Policy evaluation result (matched policy, rule, reason)
3. approval.responded — Human approved/denied (who, when, note)
4. action.executed — Gateway executed the action (timestamp, upstream status)
Generating an audit trail for a specific action
# Get all events for a specific action request
curl -H "Cookie: session=YOUR_SESSION" \
"http://localhost:3000/api/dashboard/audit-export?action_request_id=act_abc123" \
> action-trail.csvOr view in the dashboard: Actions → (click action) → Audit Timeline
The action detail page shows the complete timeline:
| Step | Event | Timestamp | Actor | |---|---|---|---| | 1 | Action requested | 2026-02-08 14:00:00 UTC | DevOps Bot | | 2 | Policy evaluated → requires_approval | 2026-02-08 14:00:00 UTC | pol_github_branch_safety | | 3 | Approval requested | 2026-02-08 14:00:01 UTC | Slack notification sent | | 4 | Approved by alice@company.com | 2026-02-08 14:02:15 UTC | alice@company.com | | 5 | Executed via gateway | 2026-02-08 14:02:16 UTC | Gateway (github connector) | | 6 | Upstream response: 200 OK | 2026-02-08 14:02:17 UTC | api.github.com |
What TameFlare can prove
| Claim | Evidence |
|---|---|
| "This action was authorized by policy" | action.allowed event with matched_policy and matched_rule |
| "A human approved this action" | approval.responded event with responded_by email and timestamp |
| "This action was blocked" | action.denied event with reason and matched_policy |
| "The kill switch was active during this period" | kill_switch.activated and kill_switch.deactivated events with timestamps |
| "This agent was suspended" | agent.suspended event with timestamp and user |
What TameFlare cannot prove
| Claim | Limitation | |---|---| | "The audit log has not been tampered with" | No cryptographic chaining. Direct DB access could modify events. | | "This is the complete set of events" | Events can be deleted via direct DB access or retention cleanup. | | "The timestamps are accurate" | Timestamps are server-side. Clock skew is possible. |
For regulatory environments requiring tamper-evident logs, export events in real-time to an external SIEM with cryptographic integrity (e.g., AWS CloudTrail, Splunk with hash verification).
Next steps
- Observability — Prometheus metrics and alerting
- Security — full security model
- Backup & Restore — database backup procedures
- Architecture — component independence and scaling