MIT Network Audit docs
The audit log

The append-only journal

When you are trying to prove where your data goes, a normal log is not enough — a log you can edit can be edited against you. The worry is simple: "Could someone quietly change this record after the fact?" The Audit Log is built so the answer is no. It is the append-only journal underneath the Trust Report: every network call MIT Network Audit captures lands here as one row, and once a row is written, it stays exactly as written.

5 min read

When you are trying to prove where your data goes, a normal log is not enough — a log you can edit can be edited against you. The worry is simple: "Could someone quietly change this record after the fact?" The Audit Log is built so the answer is no. It is the append-only journal underneath the Trust Report: every network call MIT Network Audit captures lands here as one row, and once a row is written, it stays exactly as written.

You reach it from the top menu Network Audit → Audit Log, or from the Audit Log button in the Trust Report header.

The append-only journal — one immutable row per captured network call The Audit Log list: every captured inbound and outbound call, newest first, with its direction, channel, destination, status, and the Odoo module that made it.


One captured call, one immutable row

Every inbound HTTP request to your Odoo instance, and every outbound call your modules make — to Gmail, your IMAP server, your own LLM provider, the MIT license endpoint, or anywhere else — is recorded here as a single row. There is no aggregation and no sampling at this level: the journal is the raw, per-event record. (The Traffic Summary pre-aggregates these same events for the dashboard, but it never replaces the journal underneath it.)

What makes this the trustworthy place to look is that the rows are append-only. The model behind the log (network.egress_log) rejects write and unlink — even when called with sudo, the all-powerful Odoo escalation that normally overrides access rules. There is exactly one exception: the retention vacuum, the scheduled cleanup that removes old rows once they fall outside your configured retention window. Nothing and no one else can alter or delete a row once it has been written.

Append-only is structural, not a preference

This is not a setting you could switch off. The log model refuses edits and deletes in code, and where the module is deployed it is also enforced at the database level — the Postgres table is protected against UPDATE and DELETE. The journal is designed so that "no one can rewrite history" is a property of the system, not a promise.

Append-only is only half of the guarantee. Each row is also linked to the one before it by a hash chain, so even an attempt to alter or remove a row is detectable. That tamper-evidence machinery — the chain sequence, the previous-row hash, and the row hash — is where a single row really earns its trust, and it is covered in detail on The forensic record.


Reading a row: the columns

Each row tells you, at a glance, who called what, how, and with what result — and never what was said. The list shows:

Column What it tells you
Logged When the call was captured.
Direction Inbound (a request arriving at your Odoo) or Outbound (a call your Odoo made out).
Channel The network mechanism used — for example requests, httpx, imap, smtp, pop3, or the raw socket backstop.
Host The destination host the call went to (or arrived for).
Method The operation — an HTTP method, or the protocol-level verb for mail channels.
Status The result code, where the channel reports one.
Addon The Odoo module that made the call, attributed by walking the live call stack back to its ir.module.module. This is the column that answers "which of my modules did this?".
MIT-bound Whether the destination is classified as MIT-bound (small license-validation metadata) or a third-party / your-own provider.
Req / Resp bytes The size of the request and response — the volume that moved, never the content.
Seq The row's position in the tamper-evident chain (chain_seq), a database-unique, ever-increasing serial.

The Addon and MIT-bound columns together are the heart of the trust story: they let you see, line by line, that the only traffic attributed to MIT is small and license-shaped, while your other modules talk only to your providers.


Finding the calls you care about

The list opens newest-first. Built-in filters let you narrow it to exactly the slice you want to inspect:

  • All — every captured call.
  • MIT-bound — only the calls classified as going to MIT. This is the filter to use when you want to confirm, with your own eyes, that MIT-bound traffic is nothing but small license metadata.
  • Outbound — only the calls your Odoo made out.
  • Inbound — only the requests that arrived at your Odoo.
  • Errors — only the calls that did not complete cleanly, handy for spotting a misbehaving integration.

Because every row carries its Addon, you can also group or search by module to see, for instance, every destination a particular integration has ever contacted.


What the journal does not contain

This is worth stating plainly, because it is the point of the whole product.

Bodies are never here. The journal stores metadata only. The captured event has no body field at all — there is no column to hold a request or response payload, so there is nothing to leak from this log. What you get is the shape of a call (its destination, direction, channel, method, status, sizes, timing) and its provenance (the addon and the call site) — never the contents.

So what about the sizes?

The Req / Resp bytes columns tell you how much data moved, never what it was. That is deliberate: volume is exactly the signal you need to spot something unexpected (a tiny license ping versus a large unexplained upload) without ever recording the payload itself.

The one place bodies can exist — and it is off by default

There is a separate, deliberately walled-off feature, forensic body capture, that can store request/response bodies for a legal case. It ships off by default, is officer-only, and every single read of a captured body is itself written to this journal. It is not part of the normal Audit Log you see here. See Security & access for exactly who can do what.


Where to go next

  • The forensic record — drill into a single row to see its full attribution, sizes and timing, masked headers, and the tamper-evident chain fields (Chain Seq, Prev Hash, Row Hash) that prove it has not been touched.
  • Trust Report overview — the dashboard that summarises everything in this journal into a single verdict.
  • Verifying & exporting the chain — re-check the whole chain for tampering and produce a signed proof you can hand to an auditor.