11 KiB
Data Model: Audit Log Foundation
Feature: 134-audit-log-foundation | Date: 2026-03-11
Overview
This feature extends an existing persisted entity rather than introducing a second audit-history store. The design adds a first-class event model, normalized actor and target snapshots, and a canonical read model for the Monitoring audit page.
The main model layers are:
AuditLogas the durable event record,- normalized event taxonomy and outcome semantics,
- actor and target snapshots captured at write time,
- structured context payloads with redaction,
- a workspace-scoped audit list and detail read model.
Existing Persistent Entity to Evolve
AuditLog
Current persisted shape already includes:
| Attribute | Type | Notes |
|---|---|---|
id |
int | Existing primary key |
tenant_id |
int nullable | Existing tenant scope field |
workspace_id |
int nullable | Added later for workspace-scoped events |
actor_id |
int nullable | Existing actor identifier |
actor_email |
string nullable | Existing actor display fallback |
actor_name |
string nullable | Existing actor display fallback |
action |
string | Existing event key |
resource_type |
string nullable | Existing target-type precursor |
resource_id |
string nullable | Existing target-id precursor |
status |
string | Existing outcome precursor |
metadata |
json nullable | Existing structured payload precursor |
recorded_at |
timestamp | Existing event time |
Planned first-class audit semantics to add or formalize:
| Target Semantic | Likely Storage Shape | Purpose |
|---|---|---|
event_type or normalized action |
existing action reused or renamed semantically |
Stable taxonomy key |
summary |
new string/text column | Human-readable list/detail summary |
outcome |
new column or normalized use of status |
Explicit success/failure/partial/info/blocked state |
actor_type |
new string column | Human/system/scheduled/integration distinction |
actor_label |
derived from existing name/email or new snapshot field | Stable readable actor label |
target_type |
existing resource_type reused or normalized |
Stable target taxonomy key |
target_id |
existing resource_id reused or normalized |
Structured target identity |
target_label |
new string column | Stable readable target label |
operation_run_id |
optional relation shortcut | Operational cross-linking |
context |
existing metadata reused or renamed semantically |
Structured supporting metadata |
occurred_at |
existing recorded_at reused or normalized |
Canonical event timestamp |
Core rules:
- Every row is append-only in normal product flows.
- Every row must remain understandable even when the source object is deleted or renamed later.
- Every row must be scoped at least to a workspace; tenant scope remains nullable for workspace-only events.
Supporting Domain Concepts
AuditEventType
| Field | Type | Description |
|---|---|---|
key |
string | Stable registry key such as baseline_capture.completed |
family |
string | Domain grouping such as baseline, finding, backup, restore, workspace_membership |
verb |
string | Normalized operator-facing action verb |
supports_target_link |
bool | Whether target drill-down is meaningful |
Rules:
- Must come from a shared canonical registry.
- Must cover both legacy migrated keys and new first-wave keys.
AuditOutcome
| Value | Meaning |
|---|---|
success |
Action completed successfully |
failed |
Action failed |
partial |
Action completed partially |
info |
Informational/non-mutating event |
blocked |
Action was intentionally prevented |
Rules:
- Badge rendering is centralized.
- Existing historical values like
successandfailureneed compatibility mapping where required.
AuditActorSnapshot
| Field | Type | Description |
|---|---|---|
actor_type |
string enum | human, system, scheduled, integration, platform if compatibility requires |
actor_id |
string or int nullable | Stable identifier when available |
actor_label |
string nullable | Display label captured at event time |
actor_email |
string nullable | Secondary identity where useful |
Rules:
- Human and non-human actors must be distinguishable in storage and UI.
- Historical readability must not depend on the actor record continuing to exist.
AuditTargetSnapshot
| Field | Type | Description |
|---|---|---|
target_type |
string nullable | Canonical target kind such as finding, baseline_profile, backup_set |
target_id |
string nullable | Stable identity |
target_label |
string nullable | Display label captured at write time |
workspace_id |
int | Scope guard |
tenant_id |
int nullable | Tenant guard for tenant-owned targets |
Rules:
- Target label should be snapped at write time whenever practical.
- Related links remain optional and permission-aware.
AuditContext
| Field | Type | Description |
|---|---|---|
context |
JSON-like object | Structured metadata for interpretation |
Expected content patterns:
| Pattern | Example |
|---|---|
| before/after lifecycle change | before_status, after_status |
| related run identity | operation_run_id |
| workflow rationale | reason, reason_code |
| ownership reassignment | from_assignee_user_id, to_assignee_user_id |
| summary counts | items_succeeded, items_failed |
Rules:
- Context must remain intentionally shaped.
AuditContextSanitizeror equivalent must redact secrets, tokens, and sensitive fields.- Raw policy payloads and oversized blobs are not first-class audit context.
Read Models for the Monitoring Page
AuditLogListRow
| Field | Type | Description |
|---|---|---|
id |
int | Event identity |
occurred_at |
datetime | Default sort field |
summary |
string | Primary row text |
event_type |
string | Secondary technical key |
outcome |
string | Badge-rendered state |
actor_label |
string | Who or what caused the event |
actor_type |
string | Actor-kind indicator |
target_label |
string nullable | Affected object |
target_type |
string nullable | Secondary target hint |
tenant_label |
string nullable | Only when safe to show |
workspace_id |
int | Scope |
has_related_link |
bool | Whether drill-down is available to this viewer |
Rules:
- Summary-first ordering is mandatory.
- Rows remain understandable without opening the detail view.
AuditLogFilterState
| Field | Type | Description |
|---|---|---|
search |
string nullable | High-signal summary search |
tenant_id |
string nullable | Defaulted from active tenant when entitled |
event_type |
string nullable | Taxonomy filter |
outcome |
string nullable | Outcome filter |
actor |
string nullable | Actor-label or actor-kind filter |
target_type |
string nullable | Target kind filter |
date_from |
date nullable | Range lower bound |
date_until |
date nullable | Range upper bound |
Rules:
- Filter options must come only from the active workspace and authorized tenant subset.
- Empty results are valid and intentional.
AuditLogDetailViewModel
| Field | Type | Description |
|---|---|---|
summary |
string | Primary heading |
occurred_at |
datetime | Event timestamp |
actor |
AuditActorSnapshot | Who/what caused the event |
target |
AuditTargetSnapshot nullable | Affected object |
outcome |
string | Result state |
context_items |
list | Readable context facts |
technical_metadata |
list | Secondary raw identifiers or event key |
related_link |
object nullable | Canonical target destination when permitted |
Rules:
- Readable context facts come before raw JSON.
- The event must stay intelligible if
related_linkis absent.
Existing Domain Relations Important for Coverage
Finding
Relevant persisted fields already include status, severity, assignee and owner users, due dates, and closed or resolved timestamps.
Audit significance:
- status changes
- assignee or owner changes
- risk acceptance via
STATUS_RISK_ACCEPTED - reopen, resolve, close transitions
BaselineProfile
Relevant persisted fields already include workspace ownership, status, capture mode, active snapshot, and creator.
Audit significance:
- create or update or archive actions
- capture and compare lifecycle events through related jobs and operation context
BackupSet
Relevant persisted fields already include tenant ownership, metadata, completion time, and restore-run relation.
Audit significance:
- create or update or archive events
- restore initiation linkages and backup-scope summaries
RestoreRun
Relevant persisted fields already include status, preview, results, group mapping, completion timestamps, and operation-run relation.
Audit significance:
- restore started, completed, failed, partial outcomes
- assignment outcome summaries and preview/execute distinctions when meaningful
OperationRun
Remains the canonical operational record for long-running workflows.
Audit significance:
- high-value completion/failure/retry evidence
- optional
operation_run_idshortcut fromAuditLog
State and Transition Notes
AuditLog lifecycle
Created
-> persisted as historical fact
-> never user-edited or user-deleted in normal flows
-> may be superseded or corrected only by later audit events
Workflow-to-audit relationship
User/system action occurs
-> domain service or job performs authorization and mutation
-> shared audit recorder writes one meaningful event with actor/target/context
-> canonical Monitoring page surfaces the event in reverse chronological order
Validation Rules
| Rule | Result |
|---|---|
| Every audit row has workspace scope | Required |
| Tenant-scoped events must belong to the same workspace | Required |
| Actor kind is explicit for new rows | Required |
| Summary is human-readable and non-empty for new rows | Required |
| Context is sanitized and shaped | Required |
| No user-facing edit/delete path exists | Required |
| Related links are permission-aware and optional | Required |
Schema Impact
Schema changes are expected. The preferred migration strategy is additive evolution of audit_logs with compatibility-safe backfills and indexes for:
- occurred-at or recorded-at descending review,
- workspace + occurred-at,
- tenant + occurred-at,
- event type,
- outcome,
- actor lookup,
- target lookup,
- optional operation-run shortcuts.