When an examiner asks about a screening decision from last year, two questions follow. What did the system decide? And how do you know the record you are showing me is the record that was written? The second question is the harder one.

Most systems answer it with access controls: only certain people can edit the audit table. That is necessary, but it is a promise, not evidence. In Saolix Eagle we wanted the record itself to show whether it had been changed. This piece explains how.

One transaction, or nothing

The first rule is about timing. When an analyst or a connected system screens a name interactively, three things are written: the screening result, the case it opens (if any), and the audit record. They are written in a single database transaction. If the audit record cannot be written, the whole screening rolls back, so no interactive decision exists without its record. Batch runs work differently: the whole run is recorded as one batch entry in the general audit log.

A decision without its audit record never exists, not even for a moment.

What goes into a record

Each screening audit record holds what an examiner needs to understand the decision: the name searched, how many results came back, the outcome (PASS, REVIEW or FAIL), who ran it and for which tenant, the version label of the sanctions data in force, a request identifier, and a timestamp. It also carries a freshness attestation for every list consulted: when that list was published, when it was ingested, and how old it was at the moment of screening. A reviewer can see not only what was decided but how current the lists were when it was decided.

Turning a record into a fingerprint

To seal a record, Eagle first turns it into one exact string of bytes. The fields always go in the same fixed order, each written as a key and a value, separated by control characters that cannot appear in ordinary text. Nested details are written as JSON with their keys sorted, and dates as ISO-8601. The same record always produces the same bytes, on any machine, in any language. That matters: anyone re-checking the log later must be able to rebuild exactly what was hashed.

Those bytes are then hashed. Eagle seals its log as a Merkle tree, locked on by default; a linear chain exists as a fallback, selected by server configuration:

  • A Merkle log, the default system of record. Each record becomes a leaf hash, and the leaves are combined into a tree whose root summarises the entire log. Eagle records checkpoints of the tree size and root, and each checkpoint is signed with a keyed hash (HMAC-SHA256).
  • A linear hash chain, the fallback. Each record's hash is computed over the previous record's hash plus its own bytes, using HMAC-SHA256 with a secret key. The first record starts from a hash of zeros. Change any field of any record and every hash after it stops matching.

The key matters. A plain SHA-256 structure proves a record changed, but someone with database access who knows the algorithm could quietly rebuild a valid-looking one. With a keyed hash, on the Merkle checkpoints or on every link of the chain, rebuilding it also requires a secret the database never holds. Eagle refuses to start without that secret configured.

Three audit records in a chain. Each record's hash is computed from the previous hash and its own canonical bytes with a secret key. Altering record 2 changes its hash, so record 3 no longer verifies. EACH RECORD SEALS THE ONE BEFORE IT Record 1041 prev 0000…0000 hash 7c1e…a904 Record 1042 prev 7c1e…a904 hash d93b…10ef Record 1043 prev d93b…10ef hash 52aa…7c3d HASH = HMAC-SHA256( key, previous hash ‖ canonical bytes of this record )
Illustrative values. Changing one field in record 1042 changes its hash, so record 1043's stored "prev" no longer matches.

The database refuses to forget

Hashes make tampering visible. Eagle also makes it hard. Database triggers reject updates and deletes on the audit tables, with one deliberate exception: when records pass their retention period, personal data in them can be erased under Eagle's retention process, which leaves a marker in the log. In chain mode, another trigger checks on every insert that the new record's "previous hash" really is the hash of the record before it. Eagle also defines restricted database roles that cannot update, delete or truncate anything in the audit schema; running the application under them is a configuration option.

Checking it, on demand

A seal is only useful if someone can check it. Eagle gives administrators integrity checks that recompute the log from its records: for the whole screening log, for the general audit log, or for a single request. Each returns whether the log verified, how many records were checked, and, if something is wrong, where the check failed. Administrators can also export the general audit log as a CSV report.

Checking matters. An examiner does not have to take "the log is fine" on trust: the check is recomputed from the records themselves, and it fails if something was altered.

What this gives a bank

It turns the audit conversation from assurance into evidence. Instead of explaining your access-control policy, you run the check and show the result. The decision, its record and the proof that the record is intact all come from the same system, inside your own infrastructure.

You can see what a sealed chain looks like in our sample audit chain, a short PDF with real hashes you can recompute yourself, or explore a full case in the Eagle Simulator.