Audit Logging
An immutable, attributable, tamper-evident record of all security-relevant events. Closes STRIDE Repudiation and provides the detection layer for the other five STRIDE categories. Builds on the event backbone (ADR-0006).
1. What Is Logged (and What Is Not)
| Logged | Never logged |
|---|---|
| Authentication events: login, logout, MFA challenge/pass/fail, token issue / refresh / revoke | Passwords, secrets, API key plaintext, session tokens |
| Authorization decisions — especially denials | File contents or binary data |
| Data access: file read, download, share access, bulk export | Full PII in clear text (identifiers are minimized; hash or pseudonymize where possible) |
| Mutations: file create, update, move, delete; metadata changes | Sensitive values in URL query strings (CWE-532) |
| Permission changes: role grants/revokes, member add/remove, share create/revoke | |
| Admin actions: configuration changes, tenant lifecycle events, exports | |
| Key & secret operations: create, rotate, revoke, use | |
| Policy and configuration changes |
Each audit entry carries: who (principal ID including service account), what (action), which (resource ID), when (timestamp), where (IP / device), result (success / failure), tenant (tenant ID), and trace ID — so every audit entry links directly to the full distributed trace in the observability platform.
2. Tamper-Evident, Separate-Domain Log
flowchart LR
classDef e fill:#fde68a,stroke:#b45309,color:#111827;
classDef d fill:#bbf7d0,stroke:#15803d,color:#111827;
act["security-relevant action"]:::e --> ob["transactional outbox (same DB transaction as the action)"]:::e
ob --> log["append-only Merkle audit log (separate trust domain, object-lock storage)"]:::d
log --> sth["periodic signed tree head (published)"]:::d
log --> siem["SIEM export (Splunk / Datadog / Elastic)"]:::d
sth --> proof["inclusion + consistency proofs: verify nothing edited or removed"]:::d
log --> alert["anomaly alerting: cross-tenant attempts, mass download, priv changes"]:::d
- Tamper-evident Merkle log (RFC 6962 / Certificate Transparency style): periodic signed tree heads let anyone verify that no entry was altered or deleted — even by an admin or rogue DBA.
- Separate trust domain: shipped to an isolated, append-only store with object-lock. Do not rely on cloud-native logs alone; a production compromise must not be able to reach the audit trail.
- Written via the transactional outbox (ADR-0006): the audit entry is written in the same database transaction as the action it records. There are no lost or phantom audit entries — the same correctness guarantee that domain events have.
- No update or delete API — the audit log is append-only at both the application and storage layer.
3. Structured Log Format
Every entry is structured JSON with a fixed schema:
{
"timestamp": "2026-06-12T10:30:00.000Z",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"tenant_id": "t_01j...",
"actor_id": "u_01j...",
"actor_type": "user",
"action": "file.download",
"resource_id": "n_01j...",
"resource_type": "node",
"outcome": "success",
"ip_address": "203.0.113.42",
"device_id": "d_01j..."
}
The trace_id field links every audit entry to the full OpenTelemetry trace, allowing forensic investigation to follow the complete causal chain from user action through every service call.
4. Audit as a Detection Sensor
The audit log stream is also a real-time detection feed. Alert on:
- Cross-tenant access attempts — the primary canary for isolation failures.
- Failed-auth spikes — brute force or credential stuffing in progress.
- Mass downloads or exports — potential data exfiltration.
- Privilege changes — role escalation outside normal patterns.
- Key operations — unexpected rotations or revocations.
- Impossible-travel logins — same credential from geographically implausible locations within short intervals.
- Abnormal service-account behavior — API key used outside its normal scope or volume.
5. Retention & Compliance
Retention is configurable per plan. Compliance requirements (SOC 2, GDPR, HIPAA) may impose minimum retention windows. Older entries are tiered to cold storage automatically.
Access control: the audit log is access-controlled to the auditor role and above. Tenants can access their own audit log; cross-tenant access to audit data is not permitted.
GDPR tension: audit logs contain personal data (user IDs, IP addresses). Logs use pseudonymous identifiers where possible so that a GDPR erasure request can be satisfied by revoking the mapping, leaving the audit trail’s structural integrity intact.
6. Threats Addressed
| Threat | Control | Residual |
|---|---|---|
| Repudiation (“I didn’t do that”) | Attributable, complete, append-only audit | Low |
| Audit tampering by insider or rogue DBA | Merkle tamper-evidence + separate trust domain + object-lock | Low |
| Undetected breach or exfiltration | SIEM streaming + anomaly alerting | Medium (detection coverage gap) |
| Log-based information disclosure | No secrets or PII in clear text; access-controlled to auditor role | Low |
| Lost audit entries | Transactional outbox — entry written atomically with the action | Very low |