Skip to content

Verification

AuditProofs are self-contained. You can verify one without trusting Nanorix, without an API call, and without an internet connection.


Three Verification Methods

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_hash matches step 8's chain_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.

# Python SDK — complete offline verification in one call
from nanorix.verifier import verify
result = verify(proof)
print(result.ok, result.chain_valid, result.signature_valid)
# Reference verifier binary (offline; access-gated builds during the design-partner phase)
nanorix-verify auditproof.json

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