shim-audit-verify
An independent, dependency-free verifier for Shim audit evidence bundles.
A Shim gateway writes a metadata-only audit row for every AI request, and each row's hash covers the previous row's hash. This tool takes an exported bundle of those rows and recomputes the whole chain on your machine. If a row was edited, deleted, inserted or reordered after the fact, it tells you which one.
What it does not prove
Read this before you rely on a chain OK. The full list is in
FORMAT.md.
- Anchors are local only. The daily Merkle anchors are computed and stored by the same system that writes the rows, and are not published anywhere. The chain shows rows were not edited after the fact. It proves nothing against an actor who controls both the database and the chain salt, because that actor can recompute everything from scratch. External anchor publication is on the roadmap; the product does not do it today.
- Genesis derivation is not verifiable. The chain root is derived from a per-deployment secret salt that is never published. This tool sees that row 1 links to the declared root; it cannot see that the root was derived correctly.
- A bundle is metadata. No prompt or response body is ever recorded. A bundle answers "which request, which model, which PII class, which policy outcome", never "what was sent to the model".
- This is not a declaration of conformity. It is engineering evidence. Legal sufficiency under the EU AI Act or KVKK requires independent review.
The term used throughout is tamper-evident, never "tamper-proof" or "immutable ledger", because the first is what the design achieves and the other two are not.
Quickstart
Nothing to install and no account. The tool reads the file you give it and touches the network for nothing.
$ curl -sO https://raw.githubusercontent.com/GetSHIM/shim-audit-verify/main/examples/shim-audit-sample.json
$ uvx shim-audit-verify shim-audit-sample.json
bundle shim.audit.bundle v1 org 3f2b… gateway shim-gateway/1.4.2
chain OK rows 1..305 (305 checked) anchored to genesis
anchors OK 5 verified, 0 skipped
pipx run shim-audit-verify shim-audit-sample.json works the same way, as does
pip install shim-audit-verify followed by shim-audit-verify.
Tamper demo
The second published file is a byte-for-byte copy of the first with one field
changed: row 173's prompt_tokens is one higher. Nothing else differs, and no
hash in the file was touched.
$ curl -sO https://raw.githubusercontent.com/GetSHIM/shim-audit-verify/main/examples/shim-audit-sample-tampered.json
$ uvx shim-audit-verify shim-audit-sample-tampered.json
bundle shim.audit.bundle v1 org 3f2b… gateway shim-gateway/1.4.2
chain BROKEN first break at seq 173: row_hash_mismatch
last verified seq 172
anchors OK 5 verified, 0 skipped
$ echo $?
1
The anchors still verify, and that is correct: an anchor is a tree over the stored row hashes, and this edit did not touch a row hash. The chain is what catches an edited field. The two checks cover different things, which is why the tool reports them separately.
Diff the two files yourself to confirm only that one field moved.
Usage
shim-audit-verify BUNDLE.json [--json] [--quiet]
-reads the bundle from standard input.--jsonemits the machine-readable report.--quietprints nothing; use the exit code.
Exit codes are part of the contract:
| Code | Meaning |
|---|---|
0 |
verified |
1 |
verification failed: the bundle was altered |
2 |
input error: the file is not a well-formed bundle |
1 and 2 are deliberately distinct. A malformed file must never read as
tampering, and tampering must never read as a typo.
Library
from shim_audit_verify import verify_bundle, canonical_row, chain_hash, merkle_root
report = verify_bundle(bundle_dict) # -> VerificationReport (frozen dataclass)
canonical_row, chain_hash and merkle_root are exported on purpose: they are
the reference for anyone reimplementing the format in another language.
FORMAT.md is the byte-level specification, and
tests/vectors/ holds golden vectors to check a reimplementation against. The
Shim server runs those same vectors in its own test suite, so the producer cannot
change the format without breaking this repository's contract.
Getting a bundle of your own
A Shim tenant exports one from the gateway:
$ curl -H "Authorization: Bearer $SHIM_TOKEN" \
"$SHIM_URL/api/v1/compliance/audit/bundle?start=2026-08-01T00:00:00Z&end=2026-08-31T23:59:59Z" \
-o bundle.json
$ uvx shim-audit-verify bundle.json
Development
uv sync --locked
uv run --locked ruff check .
uv run --locked ruff format --check .
uv run --locked ty check
uv run --locked coverage run -m pytest
uv run --locked coverage report
Coverage is enforced at 100% for lines and branches. See CONTRIBUTING.md.
License
Apache-2.0. See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shim_audit_verify-0.1.0.tar.gz.
File metadata
- Download URL: shim_audit_verify-0.1.0.tar.gz
- Upload date:
- Size: 15.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d6c1ef09f78190f71538333989b016116e14fa05ccf96564b7c196b9735f27b
|
|
| MD5 |
85b0caf4600769405204495709d2c84d
|
|
| BLAKE2b-256 |
5d14d3955cf25041f6c4490a47ab09c7d4c97537ddbc7ee7a36f7df8a716a97f
|
Provenance
The following attestation bundles were made for shim_audit_verify-0.1.0.tar.gz:
Publisher:
release.yml on GetSHIM/shim-audit-verify
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
shim_audit_verify-0.1.0.tar.gz -
Subject digest:
8d6c1ef09f78190f71538333989b016116e14fa05ccf96564b7c196b9735f27b - Sigstore transparency entry: 2637618968
- Sigstore integration time:
-
Permalink:
GetSHIM/shim-audit-verify@29bbc29fb19e15ea46558f50b0b241ec7e23f064 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/GetSHIM
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@29bbc29fb19e15ea46558f50b0b241ec7e23f064 -
Trigger Event:
push
-
Statement type:
File details
Details for the file shim_audit_verify-0.1.0-py3-none-any.whl.
File metadata
- Download URL: shim_audit_verify-0.1.0-py3-none-any.whl
- Upload date:
- Size: 18.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db7b6c6a266e6181e5a208d1fdcc4d9a8ba00f23dc68a46b55c8469ed97b18a8
|
|
| MD5 |
d7b3ba9c867bd738057d7dfcf785161b
|
|
| BLAKE2b-256 |
8bff5fbff0e4be13fee7c69c0ea143fd3edd7a16adea27ab157c8ee14cabc4d6
|
Provenance
The following attestation bundles were made for shim_audit_verify-0.1.0-py3-none-any.whl:
Publisher:
release.yml on GetSHIM/shim-audit-verify
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
shim_audit_verify-0.1.0-py3-none-any.whl -
Subject digest:
db7b6c6a266e6181e5a208d1fdcc4d9a8ba00f23dc68a46b55c8469ed97b18a8 - Sigstore transparency entry: 2637618987
- Sigstore integration time:
-
Permalink:
GetSHIM/shim-audit-verify@29bbc29fb19e15ea46558f50b0b241ec7e23f064 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/GetSHIM
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@29bbc29fb19e15ea46558f50b0b241ec7e23f064 -
Trigger Event:
push
-
Statement type: