# AuditEntry

## Description

AuditEntry represents a single immutable record of a data mutation captured by the audit system. Each entry records a unique `eventId` for deduplication and idempotency, the actor identity (actorType and actorId, with optional metadata such as IP address, user agent, sessionId, requestId, or onBehalfOf reference), the operation type (CREATE, UPDATE, DELETE), the affected entity reference (entityType and entityId), and a system-generated timestamp. Entries may be scoped by companyId for multi-company data isolation; entries for global entities omit companyId. An optional correlationId groups related events within a logical operation (e.g., bulk updates, cascading workflows).

Examples: "User jdoe updated SalesOrder SO-1234 at 2025-01-15T10:30:00Z", "System job recalculated exchange rates (global scope, correlationId groups all rate changes)".

## Domain Model Definitions

### Model type

AppendOnly

### Command Definitions

- [logAuditEvent](../command/LogAuditEvent.md) - Ingest an audit event, creating an AuditEntry with linked ChangeDetail records

### Query Definitions

- [getAuditEntry](../query/GetAuditEntry.md) - Retrieve a single audit entry by ID
- [searchAuditEntries](../query/SearchAuditEntries.md) - Search and filter audit entries by actor, entity, operation type, date range, and company scope
- [getAuditSummary](../query/GetAuditSummary.md) - Retrieve aggregated audit entry counts grouped by configurable dimensions

### Models

- AuditEntry
- ChangeDetail

### Invariants

- eventId is globally unique (UUID) and used for idempotency — duplicate eventId submissions are silently discarded (returns success, no new entry created)
- Once persisted, an AuditEntry cannot be modified or deleted (append-only, immutable)
- actorType is required and must be one of USER, SYSTEM, or SERVICE
- actorId is required and must be non-empty regardless of actorType
- An operation submitted without a valid actor identity (actorType + actorId) is rejected
- entityType must reference a registered auditable entity — events referencing an unregistered entityType are rejected with a validation error
- operationType must be one of CREATE, UPDATE, or DELETE
- companyId is required for company-bound entities and must be null for global entities — scope mismatches are rejected with a validation error
- timestamp is system-generated at ingestion time and cannot be supplied or overridden by the caller
- correlationId is optional (UUID) and groups related events from a single logical operation (e.g., bulk updates, cascading workflows, saga steps)
- onBehalfOf is optional and stores the delegated user's identity when an action is performed on behalf of another actor
- actorMetadata (ipAddress, userAgent, sessionId, requestId) is optional and stored when provided
- An event for an entity + operation combination with no active policy is silently discarded (no entry created)
- Audit entries are returned in chronological order by default
- Large result sets support pagination for efficient retrieval
- Querying audit entries requires the `viewAuditHistory` permission at the appropriate scope
- Querying audit summaries requires the `viewAuditSummary` permission at the appropriate scope

### Relationships

- **Has Many ChangeDetail**: Each AuditEntry has zero or more ChangeDetail records linked by auditEntryId, capturing field-level changes
- **References AuditableEntity**: entityType must match a registered AuditableEntity's entityName
- **References user-management (cross-module)**: actorId references a userId when actorType is USER; onBehalfOf references a delegated userId
- **References organization (cross-module)**: companyId references a Company from the organization module for company-scoped entries
