Skip to content

Exporting the audit trail as evidence

The audit log lives in baseline.db on the verifier, hash-chained so that nothing in it can be edited without breaking every later link. That is enough to detect tampering on a verifier you still trust. It is not enough for an auditor, and it is not enough after an incident: a chain that only exists on the machine under investigation proves nothing about that machine.

audit-export writes the chain out as a signed, self-contained artifact that verifies somewhere else, with no database, no key, no TPM and no daemon.

Writing a bundle

etminan-verifier op audit-export --out /var/backups/etminan/audit-2026-08-28.json

Like every op verb this is authorized by your kernel UID and recorded in the chain — so the export itself appears as a record in the next export. Any mapped role may run it: it reads records you could already read one at a time.

The daemon signs the bundle and hands back the bytes; your own client writes the file, owner-only (mode 0600). The daemon never learns the path. That is deliberate — a non-root daemon opening a path an operator chose would be an arbitrary-write primitive running as the account that owns the baseline database and the signing key.

The chain is verified before it is signed. A broken chain is refused rather than exported. A signature over a chain with a hole in it would be cryptographically impeccable and evidentially worthless: it would attest only that the verifier really did hold something broken.

Checking a bundle — anywhere but here

etminan-witness audit-export-verify \
    --file audit-2026-08-28.json \
    --expect-identity <64 hex chars>

Note what this command is not: it is not an op command. It opens no database, loads no key and speaks to no daemon, so it runs on a laptop, on an auditor's machine, on anything with the binary. Requiring the daemon here would mean the evidence could only be checked on the machine that produced it, which is not checking it at all.

Install etminan-witness on whatever machine that is — apt-get install etminan-witness, or dnf install etminan-witness. It is one binary with no services, no key and no configuration, and it needs neither verifier package. The same command still exists as etminan-verifier audit-export-verify on the verifier itself, which is useful for a quick look and proves nothing: evidence checked only where it was produced has not been checked.

--expect-identity is required rather than read out of the bundle, and that asymmetry is the whole point. A bundle checked against its own embedded key would verify perfectly after an attacker re-signed a rewritten chain with a key of their own. You have to bring the expected identity from somewhere else. It is the daemon's public half, printed at every start:

etminan-verifierd: listening on …; signing-key 504cfa4b0884f3…; (fail-closed: …)

Write it down once, out of band, and pin it forever.

What the reader checks, in this order:

  1. the bundle claims the identity you pinned;
  2. the signature over the whole payload verifies against it;
  3. every record's entry_hash recomputes from its own columns;
  4. seq runs contiguously from 1, and each prev_hash links to its predecessor;
  5. the head the bundle declares is the head its own records produce.

Step 4 is what makes a deletion visible. A hash chain alone does not catch one: remove a run of records, relink the survivors, and every remaining link is still intact. Requiring seq to run 1, 2, 3, … from genesis means a removed record leaves a hole that cannot be closed without rewriting every later hash — which step 2 then catches.

An empty chain is valid. A verifier that has never had a finding or an approval has nothing to export, and that is a boring state rather than a failure.

Where to keep it — and what this is not

The bundle is a periodic artifact, not a live control. It answers "here is the whole trail, provably intact as of this moment" for someone reading it later. It does not tell you today whether history was rewritten last night.

That second question is external anchoring of the audit head (ETMINAN_AUDIT_WITNESS, Enterprise): small, continuous, published to a sink the verifier cannot rewrite, and checked every cycle. The two are complements, and neither substitutes for the other:

audit-export audit head witness
what leaves the host the whole chain one (seq, hash)
how often when you run it every cycle
answers "is this trail intact?" "was history rewritten?"
needs nothing but the file a reachable sink

For retention, treat the bundle like any other backup artifact: write it to the path your backup already covers (see Backup & disaster recovery) and keep it as long as your retention policy demands. It contains the complete record of who did what on this verifier, so keep it at least as protected as the verifier itself — the 0600 it is written with is a floor, not a policy.

Sending it by mail is not the same as retaining it

A mailbox is convenient and it is off-host, but it is not append-only: a mailbox can be deleted by whoever can delete it, and in air-gapped deployments the verifier suppresses mail entirely. If you want a signal by mail, send the head and the bundle's SHA-256 rather than the trail itself — that gives you a cheap second witness without putting every actor, action and resource on this verifier into an inbox.