Verification¶
AuditProofs are self-contained. You can verify one without trusting Nanorix, without an API call, and without an internet connection.
Three Verification Methods¶
Method 1: Web Verifier (Recommended for non-developers)¶
Go to nanorix.io/verify and drag-and-drop your AuditProof JSON file.
The verifier runs entirely in your browser. No data is sent to any server. It uses the Web Crypto API for SHA-512 and an inlined Ed25519 implementation — both execute client-side.
The verifier checks:
- All 8 chain steps are present
- Hash chain integrity (each step recomputes correctly from the previous)
final_hashmatches step 8'schain_hash- The Ed25519 signature is valid against the embedded public key
Method 2: API Endpoint¶
curl -X POST https://api.nanorix.io/v1/verify \
-H "Content-Type: application/json" \
-d @auditproof.json
Response:
{
"cdp_version_valid": true,
"schema_valid": true,
"chain_integrity": true,
"final_hash_valid": true,
"canonical_hash_valid": true,
"signing_key_resolved": true,
"signature_valid": true,
"authority_status": "active",
"algorithm": "Ed25519",
"stage_reached": 8,
"verified_at": "2026-08-07T00:00:00Z"
}
This endpoint is public, requires no authentication, and is rate-limited to 60 requests per minute per IP address. It is stateless — the proof is verified and discarded. Nothing is stored.
Method 3: Offline Verification¶
For maximum independence, verify with standard cryptography libraries and no network calls. The chain formula is published in the AuditProof specification.
import json, hashlib, base64
from nacl.signing import VerifyKey
GENESIS = hashlib.sha512(b"").hexdigest()
with open("auditproof.json") as f:
proof = json.load(f)
# The 8 canonical method constants (spec-fixed; the serialized
# `operation` field is descriptive and is NOT a hash input)
METHODS = [
"procfs_verification", "mountinfo_verification",
"dod_5220_multipass_wipe", "ed25519_key_destruction",
"credential_incineration", "merkle_tree_verification",
"hash_chain_validation", "capsule_lifecycle_verification",
]
# 1. Recompute the full hash chain (all versions)
assert len(proof["chain"]) == 8, "complete proofs carry exactly 8 steps"
prev = GENESIS
ts = proof["destroyed_at"]
for step, method in zip(proof["chain"], METHODS):
material = b"\x00".join([
prev.encode(), step["subsystem"].encode(), b"destroy",
method.encode(), ts.encode(),
])
recomputed = hashlib.sha512(material).hexdigest()
assert step["chain_hash"] == f"sha512:{recomputed}", f"chain broken at step {step['step']}"
prev = recomputed
assert proof["final_hash"] == proof["chain"][-1]["chain_hash"], "final_hash mismatch"
# 2. Verify the Ed25519 signature (raises on failure)
pub = base64.b64decode(proof["attestation"]["public_key"].removeprefix("base64:"))
sig = base64.b64decode(proof["attestation"]["signature"].removeprefix("base64:"))
if proof["cdp_version"] == "1.0":
message = proof["final_hash"].removeprefix("sha512:").encode()
else: # 2.x signs the canonical document hash
message = proof["canonical_hash"].removeprefix("sha512:").encode()
VerifyKey(pub).verify(message, sig)
print("chain intact, signature valid")
Requires: pip install pynacl
Full 2.x canonical recomputation
For 2.x proofs the snippet above proves the chain recomputes and that the embedded canonical_hash is what was signed. The shipped verifiers additionally recompute canonical_hash from the document's canonical (RFC 8785) field view before checking the signature. For that complete check with one call, use nanorix.verifier.verify(proof) from the Python SDK — it runs entirely offline.
const fs = require('fs');
const crypto = require('crypto');
const nacl = require('tweetnacl');
const proof = JSON.parse(fs.readFileSync('auditproof.json'));
const sha512hex = (buf) => crypto.createHash('sha512').update(buf).digest('hex');
// 1. Recompute the full hash chain
if (proof.chain.length !== 8) throw new Error('complete proofs carry exactly 8 steps');
const METHODS = [
'procfs_verification', 'mountinfo_verification',
'dod_5220_multipass_wipe', 'ed25519_key_destruction',
'credential_incineration', 'merkle_tree_verification',
'hash_chain_validation', 'capsule_lifecycle_verification',
];
let prev = sha512hex('');
const ts = proof.destroyed_at;
proof.chain.forEach((s, i) => {
const material = Buffer.concat([
Buffer.from(prev), Buffer.from([0]),
Buffer.from(s.subsystem), Buffer.from([0]),
Buffer.from('destroy'), Buffer.from([0]),
Buffer.from(METHODS[i]), Buffer.from([0]),
Buffer.from(ts),
]);
const recomputed = sha512hex(material);
if (s.chain_hash !== `sha512:${recomputed}`) throw new Error(`chain broken at step ${s.step}`);
prev = recomputed;
});
if (proof.final_hash !== proof.chain[7].chain_hash) throw new Error('final_hash mismatch');
// 2. Verify the Ed25519 signature
const pubKey = Buffer.from(proof.attestation.public_key.replace('base64:', ''), 'base64');
const sig = Buffer.from(proof.attestation.signature.replace('base64:', ''), 'base64');
const basis = proof.cdp_version === '1.0' ? proof.final_hash : proof.canonical_hash;
const msg = Buffer.from(basis.replace('sha512:', ''));
if (!nacl.sign.detached.verify(msg, sig, pubKey)) throw new Error('signature invalid');
console.log('chain intact, signature valid');
Requires: npm install tweetnacl — or use verify() from @nanorix/sdk for the complete canonical recomputation in one call.
What Verification Proves¶
| Check | What It Confirms |
|---|---|
| Chain recomputation | Each destruction step recomputes from the previous one — no steps were inserted, removed, reordered, or edited, and the timestamp is the one the chain was built with |
| Step count | All 8 destruction steps are present — none skipped |
| Final hash | The chain terminus matches the declared final_hash — the chain was not truncated |
| Ed25519 signature | The proof was signed by the private key corresponding to the embedded public key — the signed content has not changed since signing |
What Verification Does NOT Prove¶
Verification confirms the integrity of the proof artifact. It does not independently confirm that the physical destruction operations executed on the host. The verdict is "AuditProof VERIFIED" — not "DESTRUCTION VERIFIED."
For additional provenance assurance, you can check that the public key is one Nanorix registered at signing time:
# Level 2: Provenance check (optional, requires network)
curl https://api.nanorix.io/v1/keys/{key_id}
A successful lookup confirms this public key is registered in Nanorix's key database. It does not, by itself, rule out misuse of a registered key — it anchors the proof's key to Nanorix's records.
Verification Levels¶
| Level | Method | Network Required | What It Proves |
|---|---|---|---|
| Level 1 | Offline (chain recomputation + signature) | No | Proof integrity — the artifact has not been tampered with |
| Level 2 | Key provenance (GET /v1/keys/:id) |
Yes | Proof provenance — the signing key is registered with Nanorix |