External Verification
Verify any NexArt Certified Execution Record from scratch, without the NexArt SDK or CLI.
This guide is for implementers building a verifier in a language NexArt does not ship an SDK for, or for auditors who require an independent reference implementation. It uses only standard cryptographic primitives: SHA-256, Ed25519, and a JSON canonicalizer matching the bundle's protocolVersion.
Prerequisites
- An HTTPS client.
- A JSON canonicalizer matching the bundle's
protocolVersion(nexart-v1for 1.2.0,jcs-v1/ RFC 8785 for 1.3.0). - SHA-256.
- An Ed25519 signature verifier.
- The
certificateHashof the record to verify (format:sha256:<hex>).
Step 1 — Fetch the Public Record
The public endpoint returns a redacted CER bundle. snapshot.input and snapshot.output are SHA-256 digests, not raw payloads.
curl -fsS \
"https://node.nexart.io/v1/cer/public?certificate_hash=sha256:7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069" \
-o cer.jsonStep 2 — Fetch the Node Key Set
The node publishes its current and historical public keys at a stable well-known location. Keys are identified by kid.
curl -fsS https://node.nexart.io/.well-known/nexart-node.json -o node-keys.jsonSee Key Management for the key set schema and rotation policy.
Step 3 — Select the Canonicalization Profile
Read meta.attestation.protocolVersion and select the canonicalization profile. Unknown values MUST fail closed.
"1.2.0" -> profile "nexart-v1" (default, custom canonicalization)
"1.3.0" -> profile "jcs-v1" (opt-in, RFC 8785 / JCS, standards-based)
other -> FAILEDCanonicalization is protocol-bound. Do NOT assume RFC 8785 universally; records produced with 1.2.0 use nexart-v1 and will not recompute to the same hash under JCS.
Step 4 — Recompute certificateHash (Integrity)
Project the bundle to the hashed whitelist, canonicalize with the profile selected in Step 3, and SHA-256. Compare with the bundle's declared certificateHash.
bundleType
version
createdAt
snapshot
context (only when present)
contextSummary (only when present)
policyEvaluation (only when present)import { canonicalize as jcs } from "rfc8785"; // for protocolVersion 1.3.0
import { canonicalize as nexart } from "./nexart-v1"; // for protocolVersion 1.2.0
import { createHash } from "node:crypto";
const cer = JSON.parse(fs.readFileSync("cer.json", "utf8"));
const profile = cer.meta?.attestation?.protocolVersion === "1.3.0" ? jcs
: cer.meta?.attestation?.protocolVersion === "1.2.0" ? nexart
: (() => { throw new Error("Unknown protocolVersion — FAILED"); })();
const whitelist = ["bundleType", "version", "createdAt", "snapshot",
"context", "contextSummary", "policyEvaluation"];
const projection = Object.fromEntries(
whitelist.filter(k => cer[k] !== undefined).map(k => [k, cer[k]])
);
const bytes = profile(projection);
const recomputed = "sha256:" + createHash("sha256").update(bytes).digest("hex");
if (recomputed !== cer.certificateHash) throw new Error("Integrity FAILED");Step 5 — Verify the Receipt Signature (Layer 2)
- Locate the receipt at
cer.meta.attestation.receipt. - Resolve the public key from the node key set by
receipt.kid. If not present, fail closed. - Canonicalize
receipt.payloadusing the selected profile and verify the Ed25519 signature against the canonicalized bytes. - Confirm
receipt.payload.certificateHashequalscer.certificateHash.
Step 6 — Verify the Envelope Signature (Layer 3, when present)
Reconstruct the strict whitelist projection and verify the Ed25519 signature. If the envelope is absent, report SKIPPED; this is not a failure.
{
attestation: { attestationId, attestedAt, kid, nodeRuntimeHash, protocolVersion },
bundle: { bundleType, version, createdAt, snapshot,
context?, contextSummary?, policyEvaluation? }
}Aggregate Status
VERIFIED every applicable layer = PASS
FAILED any layer = FAIL
NOT_FOUND the record does not exist on the nodeAn aggregate VERIFIED with one or more SKIPPED layers is valid and expected for sealed (local) bundles and for public, redacted bundles where Layer 3 cannot be reconstructed. See Verification Statuses and Errors.