MIT Network Audit docs
Verification

Verify & export proof

The Trust Report tells you, at a glance, that nothing is leaking to MIT. But a glance is not evidence. Sooner or later someone — an auditor, a security officer, a customer of yours, your own future self after an upgrade — will ask the harder question: can you prove the log itself has not been edited, and can you prove it to me without my having to trust your screen? This page covers the two tools that answer exactly that. The first, Verify chain, recomputes the whole tamper-e

9 min read

The Trust Report tells you, at a glance, that nothing is leaking to MIT. But a glance is not evidence. Sooner or later someone — an auditor, a security officer, a customer of yours, your own future self after an upgrade — will ask the harder question: can you prove the log itself has not been edited, and can you prove it to me without my having to trust your screen? This page covers the two tools that answer exactly that. The first, Verify chain, recomputes the whole tamper-evident chain and tells you in one line whether it is intact. The second, Export signed proof, hands you a signed file that anyone can check outside Odoo — on their own machine, with a standalone verifier, with no access to your database at all.

Both live on the header of the Trust Report, and the verify action is also reachable from the Audit Log.


Why two tools, not one

It helps to know what each one is for before you use it, because they answer two different worries.

  • Verify chain answers "has anyone tampered with my log?" — right now, live, on your own instance. It is the fast, internal check you run any time something on the report looks unusual, or simply to reassure yourself before you act.
  • Export signed proof answers "can I prove that to someone who does not trust my screen?" — it produces a portable, signed artefact that travels outside Odoo and can be re-verified independently. It is what you hand over when a glance is not enough.

You will reach for Verify chain often and Export signed proof when you need a record. They rest on the same foundation: the append-only, hash-chained forensic records and the daily-signed chain head.


Verify chain — recompute the whole chain, live

The audit log is a hash chain: every row carries a Row Hash computed over its own contents plus the previous row's hash (its Prev Hash), so a single edited row breaks the seal on every row stacked above it. Verify chain is the button that re-checks all of those seals at once.

What it actually does

When you press Verify chain, the module walks the entire network.egress_log chain and recomputes the Row Hash of every row from the row contents and the previous row's hash, then compares the recomputed value against what is stored — using a constant-time comparison (hmac.compare_digest). One of two things happens:

  • Every row matches. The chain is intact end to end, and you see the toast:

    Audit chain verified OK — no tampering detected.

  • A row does not match. The chain has been altered after the fact, and the verifier names the first row where it breaks — the earliest point at which the stored hash no longer agrees with the recomputed one. That is the row to investigate first.

It names the first tampered row on purpose

Because each seal folds in the one below it, the earliest broken row is the origin of the tampering — everything above it is "broken" only as a downstream consequence. Pointing you at the first break sends you straight to where the chain was actually disturbed, not to the noise it caused.

How to run it

  1. Open Network Audit → Trust Report (or the Audit Log).
  2. Press Verify chain in the header.
  3. Read the result: the OK toast, or the named first-tampered row.

That is the whole flow. You do not need to select anything or configure a window — Verify chain always checks the complete chain.

The Trust Report with the "Audit chain verified OK — no tampering detected" confirmation A clean result: "Audit chain verified OK — no tampering detected." A tampered log would instead name the first broken row.

Run it before you trust an unusual verdict

If the report ever shows attention or alarm (see Verdicts explained), verify the chain first. A clean verify means the report is telling you about real recorded traffic; a broken chain means you have a tampering finding to deal with as well, and you will want the row number it gives you.

Verify proves the log was not edited — not that every call was captured

A clean verify is strong evidence that what was recorded has not been changed since. It does not, by itself, prove that everything your system did was recorded. The capture is deep — down to the socket — but this is a transparency-and-detection tool, not a sealed sandbox. We say so plainly on How it works rather than imply more than the verify can claim.


Export Signed Audit — proof you can check outside Odoo

A live verify is convincing when you are the one looking. But trust is more useful when it does not depend on you at all. Export signed proof produces a signed JSON file that carries the evidence with it, so a third party can confirm the chain on their own machine — without logging into your Odoo, and without taking your word for anything.

The Export Signed Audit wizard, showing the window options and the Download and Close buttons The Export Signed Audit wizard — choose how much of the chain to include, then Download.

Choosing what goes in the file

Pressing Export signed proof opens the Export Signed Audit wizard. It lets you pick the slice of the chain to export, so a single damning row or a whole quarter is equally easy to hand over:

Window option What it exports
All period The entire chain — every row the log holds.
Date range The rows recorded between two dates you choose.
Last N days The rows from the last N days.
From chain position The rows from a chain position onward — handy when you want everything after a specific event.

When the window is set, press Download to produce the file, or Close to leave without exporting.

What the signed file contains

The export is a single JSON document, designed to be self-describing so a standalone verifier can check it with nothing but the file and the per-database signing key. It carries:

Field What it is
format The export format identifier, so a verifier knows how to read the file.
module_version The version of MIT Network Audit that produced the export.
db_name The database the rows came from.
hashed_fields Which fields of each row went into the hash — the recipe a verifier replays.
enrichment_fields The additional descriptive fields included for the human reader.
exported_window The window you chose (all period / date range / last N days / from chain position).
signed_head The signed top-of-chain — the truncation evidence (see below).
chain_seq_min / chain_seq_max The first and last chain positions covered by the export.
rows[] The exported forensic rows themselves — metadata only, no bodies, exactly as in the forensic record.

Still metadata only — even in the export

The export carries the same redacted metadata the log keeps: direction, channel, host, path, query keys, sizes, timings, masked headers, and the attribution. It does not carry request or response bodies — the captured event has no body field to begin with. Exporting a proof never exposes payload content.

How to export

  1. Open Network Audit → Trust Report and press Export signed proof (the wizard is also titled Export Signed Audit).
  2. Choose a window: All period, Date range, Last N days, or From chain position.
  3. Press Download to save the signed JSON file. (Press Close to cancel.)
  4. Hand the file to whoever needs to verify it, together with a standalone verifier.

The signed head — why deleting the tail still gets caught

The hash chain has one honest gap a careful reader will spot: a chain protects against editing a row, but what stops someone from simply deleting the most recent rows — lopping off the end of the log so a call never appears at all? On a chain alone, the remaining rows would still verify cleanly among themselves, and you would be none the wiser. The signed head closes that gap.

Once a day, a background job HMAC-signs the current head of the chain into a stored parameter (mit_network_audit.signed_head), in the form chain_seq:row_hash:hmac. The signature is made with a per-database secret that is generated on first use and never lives in the repository. Because that signed head is stored separately from the log rows it describes, it outlives them:

  • If someone truncates the tail of the log, the rows are gone — but the signed head still records that the chain once reached a higher chain_seq and a particular row_hash.
  • A verifier comparing the export's signed_head against the rows actually present can see that the chain was cut below the position the signed head attests to. The deletion leaves a hole the signature still points at.

What the export's signed_head is for

The signed_head in the exported file is precisely this truncation evidence travelling with the proof. A standalone verifier uses it to confirm two things at once: that the rows present hash-chain correctly and that the chain has not been quietly shortened since the head was signed. That is why deletion — not just editing — is detectable.

The retention vacuum never cuts at or above the signed head

The only process allowed to remove rows is the retention vacuum (governed by Audit retention (days) in Settings). By design it never deletes at or after the signed head, so legitimate housekeeping can never be mistaken for tampering — and can never erase the very rows the signed head vouches for. See Settings for how retention is configured.


Putting it together — from a hunch to a hand-over

The two tools form a short, honest workflow you can run whenever you need to move from "the report looks fine" to "here is proof anyone can check":

  1. Verify chain on the Trust Report → confirm the log on this instance is intact ("Audit chain verified OK — no tampering detected.").
  2. Export signed proof → choose All period for a full record, or a narrower window to spotlight a specific event, then Download the signed JSON.
  3. Send the file, plus a standalone verifier, to whoever asked. They re-check the chain and the signed_head outside your Odoo, on their own terms.

That last step is the whole point of shipping this module in the clear: the proof does not ask anyone to trust MIT — or even to trust you. It asks them to trust the maths, which they can run themselves.