Skill — Audit Logging
When this skill activates
Any task involving audit trails, compliance logging, change tracking, tamper detection,
event recording for accountability, or data retention policies.
Mandatory actions when this skill is active
Before implementing audit logging
- Identify what events must be audited (regulatory + business requirements).
- Define the retention policy (how long, where stored, who can access).
- Design the event schema before writing any code.
Event schema (the 5 Ws)
Every audit event MUST capture:
| Field |
Description |
Example |
| who |
user_id, IP address, session_id, service account |
{ userId: "u-123", ip: "10.0.1.5", sessionId: "sess-abc" } |
| what |
action performed, resource affected, changes made |
{ action: "update", resource: "user/u-456", changes: { email: { from: "old@x.com", to: "new@x.com" } } } |
| when |
UTC timestamp, monotonic sequence number |
{ timestamp: "2025-01-15T10:30:00Z", sequence: 1042 } |
| why |
correlation_id, request_id, triggering event |
{ correlationId: "req-789", trigger: "user_request" } |
| outcome |
success or failure, error details if failed |
{ status: "success" } or { status: "failure", error: "permission_denied" } |
Immutability guarantees
Append-only storage:
- Audit table has NO UPDATE or DELETE permissions for application roles.
- Use a dedicated audit service account with INSERT-only grants.
- Application database user must not have ALTER TABLE on audit tables.
Hash chain for tamper detection:
event.hash = SHA-256(event.data + previous_event.hash)
- Each event references the hash of the previous event.
- Broken chain = tampering detected.
- Verify chain integrity on scheduled basis (daily audit job).
Alternative: immutable storage backends:
- AWS QLDB (purpose-built immutable ledger).
- Object storage with Object Lock (S3 with WORM).
- Append-only Kafka topic with compaction disabled.
Retention policy
| Tier |
Duration |
Storage |
Access |
| Hot |
90 days |
Primary database (indexed) |
Real-time query |
| Warm |
1 year |
Object storage (Parquet/JSON) |
Query via data warehouse |
| Cold |
7+ years |
Compressed archive (Glacier/equivalent) |
Manual retrieval |
Rules:
- Define retention per event type (auth events may need longer than UI events).
- Automate tier transitions (cron job moves hot → warm → cold).
- Deletion must be cryptographic (delete encryption key, not data) for compliance.
- Document retention policy in compliance documentation.
What to audit (mandatory events)
Authentication:
- Login success and failure (with failure reason).
- Logout.
- Password change / reset.
- MFA enrollment / removal.
- Session creation and termination.
Authorization:
- Permission grants and revocations.
- Role assignments and removals.
- Access denied events.
Data mutations:
- Create, update, delete of business entities.
- Bulk operations (with count and scope).
- Data exports and downloads.
Admin actions:
- Configuration changes.
- User account management (create, disable, delete).
- System setting modifications.
Failed access attempts:
- Rate limit violations.
- Invalid token usage.
- Attempts to access other tenants' data.
Querying audit logs
Required indexes:
user_id — "show me everything user X did."
resource_id — "show me everything that happened to resource Y."
timestamp — "show me events in time range."
action — "show me all delete events."
correlation_id — "show me the full request chain."
Search capabilities:
- Full-text search on action descriptions.
- Filter by outcome (success/failure).
- Aggregate by user, resource, or time window.
Implementation patterns
Middleware/interceptor approach:
Request → [Auth] → [Audit: log attempt] → Handler → [Audit: log outcome] → Response
Event-driven approach:
- Domain events trigger audit entries asynchronously.
- Decouples audit from business logic.
- Risk: event loss if queue fails (use durable queue with DLQ).
Database trigger approach:
- PostgreSQL triggers capture all changes automatically.
- No application code needed — cannot be bypassed.
- Downside: less context (no user_id unless set in session).
Anti-patterns
- Logging sensitive data in audit trail (passwords, full credit card numbers).
- Audit log in same table/database as business data (lifecycle coupling).
- Synchronous audit blocking the business transaction.
- No alerting on audit failures (silent data loss).
- Audit logs accessible to the application for modification.
Self-check before task completion
1---2name: audit-logging3description: Skill — Audit Logging4---56# Skill — Audit Logging78## When this skill activates9Any task involving audit trails, compliance logging, change tracking, tamper detection,10event recording for accountability, or data retention policies.1112## Mandatory actions when this skill is active1314### Before implementing audit logging151. Identify what events must be audited (regulatory + business requirements).162. Define the retention policy (how long, where stored, who can access).173. Design the event schema before writing any code.1819### Event schema (the 5 Ws)2021Every audit event MUST capture:2223| Field | Description | Example |24|-------|-------------|---------|25| **who** | user_id, IP address, session_id, service account | `{ userId: "u-123", ip: "10.0.1.5", sessionId: "sess-abc" }` |26| **what** | action performed, resource affected, changes made | `{ action: "update", resource: "user/u-456", changes: { email: { from: "old@x.com", to: "new@x.com" } } }` |27| **when** | UTC timestamp, monotonic sequence number | `{ timestamp: "2025-01-15T10:30:00Z", sequence: 1042 }` |28| **why** | correlation_id, request_id, triggering event | `{ correlationId: "req-789", trigger: "user_request" }` |29| **outcome** | success or failure, error details if failed | `{ status: "success" }` or `{ status: "failure", error: "permission_denied" }` |3031### Immutability guarantees3233**Append-only storage:**34- Audit table has NO UPDATE or DELETE permissions for application roles.35- Use a dedicated audit service account with INSERT-only grants.36- Application database user must not have ALTER TABLE on audit tables.3738**Hash chain for tamper detection:**39```40event.hash = SHA-256(event.data + previous_event.hash)41```42- Each event references the hash of the previous event.43- Broken chain = tampering detected.44- Verify chain integrity on scheduled basis (daily audit job).4546**Alternative: immutable storage backends:**47- AWS QLDB (purpose-built immutable ledger).48- Object storage with Object Lock (S3 with WORM).49- Append-only Kafka topic with compaction disabled.5051### Retention policy5253| Tier | Duration | Storage | Access |54|------|----------|---------|--------|55| Hot | 90 days | Primary database (indexed) | Real-time query |56| Warm | 1 year | Object storage (Parquet/JSON) | Query via data warehouse |57| Cold | 7+ years | Compressed archive (Glacier/equivalent) | Manual retrieval |5859**Rules:**60- Define retention per event type (auth events may need longer than UI events).61- Automate tier transitions (cron job moves hot → warm → cold).62- Deletion must be cryptographic (delete encryption key, not data) for compliance.63- Document retention policy in compliance documentation.6465### What to audit (mandatory events)6667**Authentication:**68- Login success and failure (with failure reason).69- Logout.70- Password change / reset.71- MFA enrollment / removal.72- Session creation and termination.7374**Authorization:**75- Permission grants and revocations.76- Role assignments and removals.77- Access denied events.7879**Data mutations:**80- Create, update, delete of business entities.81- Bulk operations (with count and scope).82- Data exports and downloads.8384**Admin actions:**85- Configuration changes.86- User account management (create, disable, delete).87- System setting modifications.8889**Failed access attempts:**90- Rate limit violations.91- Invalid token usage.92- Attempts to access other tenants' data.9394### Querying audit logs9596**Required indexes:**97- `user_id` — "show me everything user X did."98- `resource_id` — "show me everything that happened to resource Y."99- `timestamp` — "show me events in time range."100- `action` — "show me all delete events."101- `correlation_id` — "show me the full request chain."102103**Search capabilities:**104- Full-text search on action descriptions.105- Filter by outcome (success/failure).106- Aggregate by user, resource, or time window.107108### Implementation patterns109110**Middleware/interceptor approach:**111```112Request → [Auth] → [Audit: log attempt] → Handler → [Audit: log outcome] → Response113```114115**Event-driven approach:**116- Domain events trigger audit entries asynchronously.117- Decouples audit from business logic.118- Risk: event loss if queue fails (use durable queue with DLQ).119120**Database trigger approach:**121- PostgreSQL triggers capture all changes automatically.122- No application code needed — cannot be bypassed.123- Downside: less context (no user_id unless set in session).124125### Anti-patterns126127- Logging sensitive data in audit trail (passwords, full credit card numbers).128- Audit log in same table/database as business data (lifecycle coupling).129- Synchronous audit blocking the business transaction.130- No alerting on audit failures (silent data loss).131- Audit logs accessible to the application for modification.132133## Self-check before task completion134- [ ] Did I follow the mandatory actions for this skill?135- [ ] Did I apply the patterns appropriate to the context?136- [ ] Did I verify the implementation meets the criteria above?137- [ ] Did I document decisions and trade-offs made?