There are many ways to record what happens inside an application — syslog, the ELK stack, a relational database. But Quipu-Log keeps its audit log in files embedded inside the process, with no external database or log stack. This chapter explores why files from three angles: compliance, security, and operations.
An audit log must be able to prove — after the fact — who did what, and when. The simplest implementation that satisfies that requirement is a process-embedded, append-only file with no external dependencies.
Audit logs vs. application logs
Let's separate the terms first. What we normally call an application log is for debugging. "Query took 12 ms," "connection pool at 70%" — records like these can be thrown away once the problem is solved. A few weeks is plenty, and it doesn't matter much if timestamps shift slightly over time.
An audit log has a different purpose entirely. "2026-06-14 09:42 UTC, alice viewed patient-record #4821." That fact must:
- Still be there, unchanged, when auditors come six months later.
- Make it visible if someone altered it.
- Serve as a rebuttal if alice claims "I never looked at that."
These three properties have formal names: tamper-evidence and non-repudiation. They are the defining requirements of an audit log — requirements that ordinary application logs don't carry.
What regulations demand: HIPAA · SOX · GDPR
Audit logs are often a legal obligation, not just a nice-to-have.
- HIPAA (U.S. health data protection) — who accessed patient data must be retained for six years, and record integrity must be maintained.
- SOX (corporate accounting transparency) — the change history of financial systems must be retained for seven years.
- GDPR (European data protection) — records of personal-data processing activities and access logs must be kept.
The common thread across all of these: you must be able to prove that records haven't changed since they were written. Not just "the record exists," but "the record is exactly as it was when first written."
Quipu-Log's tamper-evidence design starts directly from this requirement. The Merkle tree covered in Part 5 (Ch. 19–23) makes records "obviously wrong if edited" — not by preventing edits, but by ensuring that any edit leaves a visible trace. That is precisely what regulations ask for.
Why not a database or ELK?
Storing audit logs in a database seems intuitive — it gives you transactions, easy queries, and access control. But there are a few problems.
| Approach | Strengths | Problems for audit logs |
|---|---|---|
| Relational DB (same instance) | Transactions, SQL queries | If it's the same DB, a DBA can DELETE — an insider can erase records. When the service goes down, audit recording stops too. |
| External DB (separate instance) | Separate trust boundary | Operational overhead. You have to manage another DB just for audit logs. Network failure means an audit gap. |
| ELK / log stack | Search and visualization | Elasticsearch overwrites indexes. Tamper-evidence is fundamentally hard. Cost and operational burden are also high. |
| Quipu-Log (embedded files) | No dependencies, simple, embedded | Works wherever the OS filesystem does. Runs inside the service — network failure is irrelevant. |
The key concept is the trust boundary. If the audit log lives in the same process or database as the service, the people operating that service (insiders) can delete records. Quipu-Log tackles this in two ways. First, it writes directly to files in append-only mode (the code has no overwrite path). Second, a Merkle tree and external anchoring detect tampering at the disk level.
Think of the room in a bank branch where tellers handle cash. Every transaction in that room goes into a separate locked vault — a vault where neither the teller nor the branch manager can erase a record once it's written. The idea is that audit logs should be "inside the same system, but impossible to alter."
Embedded mode: running inside the process
Quipu-Log runs embedded. Rather than a separate daemon or service, it executes inside your Rust service's own process. Just cargo add quipu-core and call AuditStore::open() — that's all it takes.
README.md — Quick startlet root = std::path::PathBuf::from("/var/lib/myapp/audit");
let cfg = StoreConfig::new(&root)
.retention(RetentionPolicy::days(90))
.sync_policy(SyncPolicy::EveryN(32));
let mut store = AuditStore::open(cfg)?;
// That's it — no daemon, no separate DB.
Of course, if you need a central audit store shared by multiple services, there's also a server mode where quipu-server runs as a separate process. When to choose embedded vs. server mode is covered in detail in Ch. 2.
In a DB, audit logs are often stored as INSERT rows in a table — and the same DB administrator can DELETE them, which is the weakness. In Quipu-Log, the storage engine itself is an append-only file, and there is no overwrite path at the code level. The design isn't "we prevent it" — it's "if you change it, the change shows."
What gets recorded: the event structure
A Quipu-Log audit event captures what actor did what action (method, url) to what target. The actor is "who" — a user, a service account. The target is "what" — a document, a patient record, an account. This structure lets you query the same records in two directions: "who did what to this document?" and "what did this user touch?"
Quipu-Log is tamper-evidence, not tamper-prevention. An attacker with physical access to the disk can edit the file. Quipu-Log detects that tampering. SECURITY.md states this scope explicitly: "an attacker with write access to the store changed a record" — if verify_integrity doesn't catch that, it's a vulnerability, but the inability to prevent it is by design.
① Name two decisive differences between an audit log and an application log.
② What is the trust-boundary problem with storing audit logs in the same database as your service?
③ How do tamper-evidence and tamper-prevention differ? Which one is Quipu-Log?