Audit Log
Data Entity
Description
Tamper-evident, append-only chronological record of security-relevant events across the Meander platform. Captures actor identity, target entity, outcome, and contextual metadata for every authentication, authorization, administrative, and data-access action. Scoped per organization for tenant isolation; global admins have cross-tenant read access.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Immutable primary key generated at insert time. Never reused. | PKrequiredunique |
organization_id |
uuid |
Tenant scope. FK to organizations. Null only for system-level events that span no single tenant (e.g. global admin login). | - |
actor_id |
uuid |
User who performed the action. FK to users. Null for automated system events (e.g. scheduled jobs, cron-triggered exports). | - |
actor_role |
enum |
Role the actor held at the time of the event. Stored denormalized so role changes do not alter historical records. | required |
impersonated_user_id |
uuid |
Populated in proxy/bulk-registration scenarios where a coordinator acts on behalf of a peer mentor. Records the subject user, while actor_id records the coordinator. | - |
event_category |
enum |
High-level grouping used for dashboard filtering and anomaly detection. | required |
event_type |
enum |
Specific event that occurred. Determines which metadata fields are populated. | required |
outcome |
enum |
Result of the action. Failed and denied outcomes are equally important to record. | required |
target_entity_type |
string |
Type of the primary entity that was acted upon (e.g. 'user', 'activity', 'expense_claim', 'session', 'assignment', 'module_configuration'). | - |
target_entity_id |
string |
ID of the affected record, stored as string to accommodate UUIDs and composite keys alike. Nullable for events with no single target (e.g. bulk actions recorded separately per affected row). | - |
action_description |
string |
Human-readable one-line summary of what happened, pre-rendered at write time so the log is interpretable without joining other tables. | required |
ip_address |
string |
IP address of the request origin. Supports both IPv4 and IPv6. Null for system-initiated events. | - |
user_agent |
string |
HTTP User-Agent header from the request. Used to distinguish mobile app, admin portal, and API client actions. | - |
session_id |
uuid |
Session that was active when the event occurred. Not a FK (sessions may be revoked/deleted; the audit log must survive session deletion). | - |
metadata |
json |
Event-type-specific supplementary data. Schema varies by event_type. Examples: {changed_fields: [...], previous_value: ..., new_value: ...} for config changes; {affected_user_ids: [...]} for bulk actions; {module_id: ..., enabled: true} for module toggles. | - |
checksum |
string |
HMAC-SHA256 of the row's core fields (id, actor_id, event_type, target_entity_id, created_at, metadata) using a server-side secret. Allows tamper detection during compliance review. | required |
created_at |
datetime |
UTC timestamp when the event was recorded. Set by the database at insert; never client-supplied. | required |
Database Indexes
idx_audit_logs_org_created
Columns: organization_id, created_at
idx_audit_logs_actor_id
Columns: actor_id
idx_audit_logs_event_type
Columns: event_type
idx_audit_logs_event_category
Columns: event_category
idx_audit_logs_target_entity
Columns: target_entity_type, target_entity_id
idx_audit_logs_created_at
Columns: created_at
idx_audit_logs_outcome
Columns: outcome
idx_audit_logs_session_id
Columns: session_id
Validation Rules
required_fields_present
error
Validation failed
event_type_category_consistency
error
Validation failed
organization_id_required_for_org_events
error
Validation failed
ip_address_format
error
Validation failed
created_at_server_only
error
Validation failed
metadata_size_limit
error
Validation failed
no_pii_in_action_description
warning
Validation failed
Business Rules
append_only
Audit log rows are immutable once written. No UPDATE or DELETE statements are permitted on audit_logs. Any attempt must result in a hard error, not a silent no-op. The audit-append-enforcement component enforces this at the database layer via a trigger or RLS policy.
tenant_isolation_read
Org admins and coordinators may only query audit_logs where organization_id matches their session's organization. Global admins may query across all organizations. audit-query-service enforces this via org-scoped JWT claims before executing any query.
checksum_on_insert
The checksum field must be computed and stored by audit-service at insert time using a server-side HMAC key. The checksum is never computed client-side. Rows missing a checksum are rejected.
proxy_actor_capture
When a coordinator registers activity on behalf of a peer mentor (proxy/bulk registration), both actor_id (coordinator) and impersonated_user_id (peer mentor) must be recorded. A log entry with impersonated_user_id set must always also have actor_id set.
system_events_null_actor
Events generated by scheduled jobs or system processes set actor_id to null and actor_role to 'system'. No event may have actor_role='system' with a non-null actor_id.
sensitive_access_always_logged
Any read of encrypted assignment content (Encrypted Data Assignments area) must produce an audit_logs entry with event_type='assignment_read'. This is enforced by the assignment-dispatch-service and encryption-service, not at the DB layer.
audit_log_access_self_logged
When an admin queries the audit log, an audit_logs entry of event_type='audit_log_accessed' is created for that query. This creates a complete chain of custody without recursion (the self-log entry is not itself logged).
rule_change_always_logged
Any change to auto-approval rules, module configurations, organization labels, or notification rules must produce an audit entry via rule-change-audit-logger before the change is committed. If the audit write fails, the config change is rolled back.