Verification¶
CDPs are self-contained. You can verify a CDP 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 CDP JSON file.
The verifier runs entirely in your browser. No data is sent to any server. It uses the Web Crypto API for SHA-256 and TweetNaCl.js for Ed25519 — both execute client-side.
The verifier checks:
- All 8 chain steps are present
- Hash chain integrity (each step correctly links to the previous)
final_hashmatches step 8'schain_hash- Ed25519 signature is valid against the embedded public key
- No tampering detected
Method 2: API Endpoint¶
curl -X POST https://api.nanorix.io/v1/verify \
-H "Content-Type: application/json" \
-d @destruction_proof.json
Response:
{
"valid": true,
"checks": {
"chain_integrity": true,
"final_hash": true,
"signature": true,
"step_count": 8
}
}
This endpoint is public, requires no authentication, and is rate-limited to 60 requests per minute per IP address. It is stateless — the CDP is verified and discarded. Nothing is stored.
Method 3: Offline Verification¶
For maximum independence, verify using standard cryptography libraries with no network calls.
import json, hashlib, base64
from nacl.signing import VerifyKey
with open("destruction_proof.json") as f:
cdp = json.load(f)
# Verify hash chain integrity (genesis = SHA-256 of empty string)
prev = hashlib.sha256(b"").hexdigest()
for step in cdp["chain"]:
assert step["chain_hash"].startswith("sha256:")
prev = step["chain_hash"].split(":", 1)[1]
# Verify final_hash matches last chain step
last_chain = cdp["chain"][-1]["chain_hash"]
assert cdp["final_hash"] == last_chain
# Verify Ed25519 signature — key is embedded, no network needed
pub = base64.b64decode(
cdp["attestation"]["public_key"].replace("base64:", ""))
sig = base64.b64decode(
cdp["attestation"]["signature"].replace("base64:", ""))
VerifyKey(pub).verify(
bytes.fromhex(cdp["final_hash"].replace("sha256:", "")),
sig
)
print("VALID — Chain intact · Signature verified · Proof authentic")
Requires: pip install pynacl
const fs = require('fs');
const crypto = require('crypto');
const nacl = require('tweetnacl');
const cdp = JSON.parse(fs.readFileSync('destruction_proof.json'));
// Verify hash chain
let prev = crypto.createHash('sha256').update('').digest('hex');
for (const step of cdp.chain) {
const hash = step.chain_hash.replace('sha256:', '');
// Each step's chain_hash should be deterministically computed
prev = hash;
}
// Verify final_hash matches last chain step
const lastHash = cdp.chain[cdp.chain.length - 1].chain_hash;
console.assert(cdp.final_hash === lastHash, 'Final hash mismatch');
// Verify Ed25519 signature
const pubKey = Buffer.from(
cdp.attestation.public_key.replace('base64:', ''), 'base64');
const sig = Buffer.from(
cdp.attestation.signature.replace('base64:', ''), 'base64');
const msg = Buffer.from(
cdp.final_hash.replace('sha256:', ''), 'hex');
const valid = nacl.sign.detached.verify(msg, sig, pubKey);
console.log(valid ? 'VALID' : 'INVALID');
Requires: npm install tweetnacl
use ed25519_dalek::{VerifyingKey, Signature, Verifier};
use base64::Engine;
use base64::engine::general_purpose::STANDARD;
// Parse CDP from JSON...
// Verify Ed25519 signature
let pub_bytes = STANDARD.decode(
cdp.attestation.public_key.strip_prefix("base64:").unwrap()
).unwrap();
let sig_bytes = STANDARD.decode(
cdp.attestation.signature.strip_prefix("base64:").unwrap()
).unwrap();
let msg = hex::decode(
cdp.final_hash.strip_prefix("sha256:").unwrap()
).unwrap();
let key = VerifyingKey::from_bytes(&pub_bytes.try_into().unwrap()).unwrap();
let sig = Signature::from_bytes(&sig_bytes.try_into().unwrap());
key.verify(&msg, &sig).expect("Signature verification failed");
println!("VALID — Signature verified");
What Verification Proves¶
| Check | What It Confirms |
|---|---|
| Chain integrity | Each destruction step is correctly linked to the previous one — no steps were inserted, removed, or reordered |
| Final hash | The chain terminus matches the declared final_hash — the chain was not truncated |
| Ed25519 signature | The CDP was signed by the private key corresponding to the embedded public key — the proof has not been modified since signing |
| Step count | All 8 destruction steps are present — no steps were skipped |
What Verification Does NOT Prove¶
Verification confirms the integrity of the proof artifact. It does not independently confirm that the physical destruction operations actually executed. The verdict is "CDP VERIFIED" — not "DESTRUCTION VERIFIED."
For additional provenance assurance, you can verify that the public key exists in Nanorix's key database:
# Level 2: Provenance check (optional, requires network)
curl https://api.nanorix.io/v1/keys/{key_id}
This confirms the key was generated by Nanorix's infrastructure, not by an attacker who generated their own keypair.
Verification Levels¶
| Level | Method | Network Required | What It Proves |
|---|---|---|---|
| Level 1 | Offline (hash chain + signature) | No | Proof integrity — artifact has not been tampered with |
| Level 2 | Key provenance (GET /v1/keys/:id) |
Yes | Proof provenance — key was generated by Nanorix infrastructure |