# 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: 1. `AuditLog` as the durable event record, 2. normalized event taxonomy and outcome semantics, 3. actor and target snapshots captured at write time, 4. structured context payloads with redaction, 5. 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 `success` and `failure` need 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. - `AuditContextSanitizer` or 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_link` is 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_id` shortcut from `AuditLog` ## State and Transition Notes ### AuditLog lifecycle ```text 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 ```text 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.