nexart.iodocs

    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-v1 for 1.2.0, jcs-v1 / RFC 8785 for 1.3.0).
    • SHA-256.
    • An Ed25519 signature verifier.
    • The certificateHash of 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.

    Fetch public CER
    curl -fsS \
      "https://node.nexart.io/v1/cer/public?certificate_hash=sha256:7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069" \
      -o cer.json

    Step 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.

    Fetch node public keys
    curl -fsS https://node.nexart.io/.well-known/nexart-node.json -o node-keys.json

    See 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   -> FAILED

    Canonicalization 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.

    Hashed projection (whitelist)
    bundleType
    version
    createdAt
    snapshot
    context           (only when present)
    contextSummary    (only when present)
    policyEvaluation  (only when present)
    Reference (pseudo-code) — example assumes a 1.3.0 (jcs-v1) bundle
    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)

    1. Locate the receipt at cer.meta.attestation.receipt.
    2. Resolve the public key from the node key set by receipt.kid. If not present, fail closed.
    3. Canonicalize receipt.payload using the selected profile and verify the Ed25519 signature against the canonicalized bytes.
    4. Confirm receipt.payload.certificateHash equals cer.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.

    Envelope signed projection
    {
      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 node

    An 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.