The standalone verifier
A capital figure from the FRTB SA Calculator isn't a number — it's a node in a Merkle DAG anchored to its inputs and to the function identity that produced it. Drift any of those, and the root hash changes.
The verifier is the audit-side companion. It reads a saved lineage JSON, recomputes every claimed hash from its claimed payload, and either accepts the chain or refuses it with the exact node where the chain breaks. It depends on nothing except Python's standard library.
Why it exists as a separate file
An auditor or regulator should be able to verify a capital figure on their own laptop, with no access to the running system that produced it. The verifier we ship inside the backend is the same logic, but bound to FastAPI, SQLAlchemy, and our deployment. The standalone file is a single artefact you can email, hand on a USB stick, or drop into an air-gapped review environment.
It is intentionally Python-only. Any reviewer can inspect what it does in roughly 200 lines. There is no compiled component, no obfuscation, no required upgrade. The version that verifies your lineage today will verify it in 2030.
How to run it
Save the file (link below), then:
python verify_lineage.py path/to/lineage.json
python verify_lineage.py --expected-function-reference SHA path/to/lineage.json
cat lineage.json | python verify_lineage.py -
Exit codes:
0— admissible. The chain holds end-to-end.1— refused. A mismatch was found; the broken node's sha256 is printed.2— bad input. The file could not be read or parsed as JSON.
The chain it replays
The lineage is a three-tier DAG. The verifier walks it from the leaves up to the root, hashing every payload as it goes.
input_leaf
One row of canonical input — the risk factor name, class,
bucket, delta, and a reference to the schema contract. Hashed
into the leaf's sha256.
function_node
The engine identity — engine version, regime calibration,
parameter-set hash — bound to the sha256s of every input
leaf it consumed. The leaf list in its payload must equal
the children claimed by its edges.
output_root
The capital figure plus its function_node sha256. The root
hash is what gets quoted in the Excel export, the audit log,
and the explanation. Drift any leaf, the function, or the
regime, and this hash changes.
What it catches
-
HASH_MISMATCH— any byte mutation in any node's canonical payload, flagged at the tampered node. -
EDGE_LEAF_MISMATCH— a function_node whose payload claims a different set of leaves than its edges actually carry. -
EDGE_FUNCTION_MISMATCH— an output_root pointing at a function_node not present in its edges. -
FUNCTION_REFERENCE_DRIFT— when you supply--expected-function-reference, the function_node's engine + regime + parameter-set hash must match. Silent calibration changes are caught here. -
ROOT_NOT_IN_NODE_SET— a lineage whose claimed root has no matching node, or no root at all.
Every failure mode has at least one regression test against a deliberately tampered fixture. CI runs the verifier on a fresh demo lineage on every push and proves both the happy path and a one-byte tamper.
Worked example
Here is the verifier's output for a clean and a tampered lineage, on the demo dataset of three FX deltas:
$ python verify_lineage.py demo.json
======================================
admissible : True
reason : OK
======================================
$ echo $?
0
$ python verify_lineage.py tampered.json
======================================
admissible : False
reason : HASH_MISMATCH at
kind=input_leaf
broken_node_sha256 : ba56df41…463d3b3d
======================================
$ echo $?
1
Operational use
The convention we recommend: every time a capital figure is published to a stakeholder, the saved lineage and the verifier travel with it. Whoever receives the figure can replay it independently, in seconds, on their own machine.
- Auditors in model-risk review verify capital figures offline, with no access to your runtime.
- Regulators during examination confirm provenance integrity from the JSON alone.
- Internal model-risk include the verifier invocation in the standard sign-off pack.
Get the file
The verifier is shipped with the product. It lives at
tools/verify_lineage.py in the source tree, and the
marketing service hosts an exact copy alongside this page so
regulators can grab it without a code-host login.
Download verify_lineage.py View source in browser
The product itself is sold as self-hosted software. To run the calculator, see the self-hosting section on the home page or get in touch.