TenantAtlas/specs/134-audit-log-foundation/data-model.md
ahmido 28cfe38ba4 feat: lay audit log foundation (#163)
## Summary
- turn the Monitoring audit log placeholder into a real workspace-scoped audit review surface
- introduce a shared audit recorder, richer audit value objects, and additive audit log schema evolution
- add audit outcome and actor badges, permission-aware related navigation, and durable audit retention coverage

## Included
- canonical `/admin/audit-log` list and detail inspection UI
- audit model helpers, taxonomy expansion, actor/target snapshots, and recorder/builder services
- operation terminal audit writes and purge command retention changes
- spec 134 design artifacts and focused Pest coverage for audit foundation behavior

## Validation
- `vendor/bin/sail bin pint --dirty --format agent`
- `vendor/bin/sail artisan test --compact tests/Unit/Audit tests/Unit/Badges/AuditBadgesTest.php tests/Feature/Filament/AuditLogPageTest.php tests/Feature/Filament/AuditLogDetailInspectionTest.php tests/Feature/Filament/AuditLogAuthorizationTest.php tests/Feature/Monitoring/AuditCoverageGovernanceTest.php tests/Feature/Monitoring/AuditCoverageOperationsTest.php tests/Feature/Console/TenantpilotPurgeNonPersistentDataTest.php`

## Notes
- Livewire v4.0+ compliance is preserved within the existing Filament v5 application.
- No provider registration changes were needed; panel provider registration remains in `bootstrap/providers.php`.
- No new globally searchable resource was introduced.
- The audit page remains read-only; no destructive actions were added.
- No new asset pipeline changes were introduced; existing deploy-time `php artisan filament:assets` behavior remains unchanged.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #163
2026-03-11 09:39:37 +00:00

281 lines
11 KiB
Markdown

# 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.