Verification
How to verify a record yourself
Every action on a case is written to an append-only ledger. Each entry is a SHA-256 hash that also covers the entry before it, so changing any past entry breaks every hash after it. Only these hashes ever leave our database. No names or medical details are published.
We prove when each entry existed in two independent ways:
- Our own Bitcoin anchor (primary). About every 6 hours we combine all new entry hashes into a Merkle tree and write its 32-byte root into a Bitcoin transaction that we sign and pay for ourselves. The data is in an OP_RETURN output: the 4 bytes NOK1 followed by the root.
- OpenTimestamps (backup). Every 30 minutes the same kind of root is also sent to the public OpenTimestamps calendars, which commit it to Bitcoin too. The verify page offers that .ots proof for the standard ots verify tool.
Check the Bitcoin anchor in four steps
Scan the QR code on a document (or open its /verify/… link) and download the Bitcoin anchor proof (JSON). Then:
- Recompute the entry hash. sha256_hex(prevHash + "\n" + content), using the literal text GENESIS if there is no previous hash. It must equal entry.hash. For a PDF, you can also hash the file and compare it to the PDF SHA-256 shown on the verify page.
- Follow the Merkle path. Start with h = the entry hash as 32 raw bytes. For each step, if the side is R compute h = sha256(h ‖ sibling); if it is L compute h = sha256(sibling ‖ h). The result must equal merkle.root.
- Look up the transaction. Open bitcoin.txid on any block explorer (for example mempool.space or blockstream.info) or your own node. One output has the script 6a 24 4e4f4b31 <root>, which is OP_RETURN, a 36-byte push, NOK1, then the root. The root must match yours.
- Note the block. The block containing that transaction gives the latest time the entry could have been created. After that, nobody (including us) can change it without the mismatch showing.
Example in Python
import hashlib, json
p = json.load(open("proof.json"))
e = p["entry"]
assert hashlib.sha256(((e["prevHash"] or "GENESIS") + "\n" + e["content"]).encode()).hexdigest() == e["hash"]
h = bytes.fromhex(e["hash"])
for s in p["merkle"]["path"]:
sib = bytes.fromhex(s["hash"])
h = hashlib.sha256(h + sib if s["side"] == "R" else sib + h).digest()
assert h.hex() == p["merkle"]["root"]
print("Now confirm tx", p["bitcoin"]["txid"], "has OP_RETURN", "6a244e4f4b31" + h.hex())The verify page and proof file never contain patient or family information, only document type, facility, times and hashes.