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

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:


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