Audit Explorer
Every decision Suvra makes is recorded as a structured audit event. The Audit Explorer (/dashboard/audit) is where you investigate what an agent did, why a rule matched, and whether an approval was issued or consumed.
Requires the audit.read permission.
What's in an audit event
Each row in audit_events carries:
request_id— correlates API call, dashboard request, and node spool eventagent_id,user,role,environment,tenant_id,business_unit,domain,labels— identity contextaction_type+params— the raw action payloaddecision—allow/deny/needs_approval/monitor/disabledmatched_rule_id,policy_id,policy_version,policy_scope_level,policy_scope_identifierresult_summary— structured explainability (the same JSON the simulator returns)rollback_payload,rollback_available— captured by executors when applicablestatus—validated,executed,denied,rolled_back, etc.
Filters
The filter bar at the top of the page accepts:
| Filter | Type | Notes |
|---|---|---|
Search (q) | free text | Matches actor, action, target, result fields |
| Actor | text | e.g. user:alice@example.com |
| Action Type | text | e.g. fs.write_file, http.request |
| Decision | text | allow / deny / needs_approval |
| Status | text | validated / executed / denied / rolled_back / ... |
| Agent | text | agent_id, e.g. codex |
| Domain | text | Policy domain |
| Environment | text | production, staging, ... |
| Policy id | text | Trace what a specific policy blocked/allowed |
| Policy pack | text | Starter pack identifier |
| Denied only | toggle | decision=deny rows only |
| Approval required | toggle | decision=needs_approval rows only |
| Page size | select | 25 / 50 / 100 / 200 |
Pagination links omit inactive boolean filters so URLs stay stable as you navigate.
Events table
| Column | Description |
|---|---|
| Time | UTC timestamp, rendered in viewer's locale |
| Actor | User or agent that made the request |
| Action | Human-readable action title |
| Target | Action target (path, URL, etc.) in code format |
| Decision | Badge: Allowed / Blocked / Needs approval |
| Status | Badge: validated / executed / denied / rolled_back |
| Rollback | Inline rollback button when rollback_available=true, else "none" |
| Detail | Opens the decision-trace drawer |
Detail drawer
Clicking Detail opens a drawer with:
- Decision hero — decision display, summary, and badges
- Key fields — action-type-specific (Path, Bytes, Method, Target, Timeout, Dry-run, ...)
- Identity fields — Node, Node Event, Agent, User, Role, Workspace, Environment, Tenant, Business Unit, Domain, Labels
- Business explanation — bullets derived from
result_summary - Reasons — raw reason codes
- Decision trace — Final decision, reason, winning rule, Loaded Layers table (Order / Policy / Source / Scope), Overridden Rules table (Rule / Scope / Effect / Reason)
- Rollback — replay the stored
rollback_payload
Export
Export buttons render when the user holds the audit.export permission:
- Export CSV —
GET /audit.csv - Export JSON —
GET /audit.json(newline-delimited)
Both endpoints respect the current filter set and use the audit rate-limit bucket.
Rollback
When a row has rollback_available=true, the Rollback button replays the stored rollback_payload through the originating executor and writes a follow-up audit event (rolled_back, not_found, rollback_failed, or rollback_payload_corrupt).
POST /actions/{action_id}/rollback (with optional {"dry_run": true}) performs the same operation from the API.
Centralized ingest
In distributed deployments, enforcement nodes spool audit events locally and then flush them to POST /control/audit/ingest. The dashboard reads from the same audit_events table, so node-originated events appear inline with control-plane-native records once ingested.