inhold Book a call

Evidence pack

What is in an Inhold evidence pack, and how do I check it?

An Inhold evidence pack is a zip of the policy, every audit row and the chain check. Download a real one from the synthetic demo and check it yourself.

Last checked 31 Oct 2026, written by Vivek

Inhold is being built. No customers yet. Repo goes public 9 Nov 2026.

Download

A real pack from the synthetic demo

Made by python -m demo.run_demo on main 478aded, 31 Oct 2026. The hospital, patients and people are synthetic. 14 files, 20 KB.

Download the pack verify_chain.py

MANIFEST.json

Every file with its SHA-256, the chain head, the anchor written at export and the policy hash.

audit/audit_events.jsonl

All 24 audit rows from the demo run, allowed and denied.

audit/chain_verification.json

The chain check at export: ok, 24 rows, 3 anchors, signed, 0 rows after the newest anchor.

anchors/anchors.jsonl

The signed chain heads. The demo signs them with a synthetic demo key.

policy/active_bundle.json

The hospital's policy that made every decision, with its hash beside it.

CONTROL_MAP.md

Each 164.312 line, what Inhold does, the evidence file and the status.

TOP20_ANSWERS.md

The 20 reviewer answers, with the one that needs a lawyer marked.

THREAT_MODEL.md, DATA_FLOW.md, HASHING.md

What is defended, how data moves and how to check the chain.

deployment/egress_statement.md

Every outbound call the service can make.

Check it

Recompute the chain with 12 lines of Python

No Inhold install needed, only the standard library. It rebuilds each row's hash from its fields and checks that each row points at the one before.

$ python3 verify_chain.py inhold-evidence-pack-demo.zip
24 rows chain correctly, head 45a1e0cb6846e2e1
import hashlib, json, sys, zipfile
FIELDS = ("seq", "ts", "tenant_id", "actor_type", "actor_id", "action", "object_id",
          "labels", "purpose", "decision", "reason", "policy_hash", "prev_hash")
OPTIONAL = ("engine_hash", "session_id")
rows = [json.loads(l) for l in zipfile.ZipFile(sys.argv[1]).read("audit/audit_events.jsonl").splitlines()]
prev = "0" * 64
for r in rows:
    f = {k: r[k] for k in FIELDS} | {k: r[k] for k in OPTIONAL if r.get(k) is not None}
    h = hashlib.sha256(json.dumps(f, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
    assert r["prev_hash"] == prev and r["row_hash"] == h, f"chain breaks at row {r['seq']}"
    prev = h
print(f"{len(rows)} rows chain correctly, head {prev[:16]}")

Tamper test

Flip one denial to allow, and the chain breaks at that row

Row 20 is the billing agent refused a prior authorization note. We changed its decision to allow and ran the same script. Animation shows the edit; the result is below it.

SeqActorActionPurposeDecisionRow hash
18prior-auth-agentsearchprior_authallow829f883efe
19prior-auth-agentreadprior_authallow0fe586b37a
20billing-agentreadbillingdeny1c6eb2183e
21billing-agentreadprior_authdenyff05583529
22secops.okaforexportallow0c4608e27c
$ python3 verify_chain.py tampered.zip
AssertionError: chain breaks at row 20

Someone who can write to the database could instead recompute every hash after the edit. The chain would then check out, which is why Inhold also writes signed anchors outside the database. The demo checks the rewritten pack against anchors from an earlier pack and gets ok=False. Anchors in S3 with KMS signing are in code and not yet run on real AWS; read why every audit row is chained.