Verification
How to verify any NexArt record, with or without API access.
All verification results derive from this model.
NexArt Integration (Canonical Flow)
Flow:
- Capture execution input
- Seal locally (offline)
- Verify locally
- (Optional) Certify via node
- Verify again
Verification outcomes:
- Sealed → Integrity: PASS, Receipt: SKIPPED, Envelope: SKIPPED
- Certified → Integrity: PASS, Receipt: PASS, Envelope: PASS
Rules:
- Sealing does NOT require a node
- Certification is OPTIONAL
- Verification does NOT require trust
- SKIPPED is expected for local artifacts and is NOT a failure
Do NOT:
- call the node before sealing
- recompute hashes manually
- modify bundles after sealing
- treat SKIPPED as failure
Technical truth
- What a CER is
- A Certified Execution Record (CER) is a canonical, tamper-evident bundle representing one execution. It has
bundleType: "cer.ai.execution.v1", aversion, acreatedAtISO-8601 timestamp, asnapshot(model, inputHash, outputHash, metadata), and an optionalcontext/contextSummary. The CER is identified by itscertificateHash(SHA-256). Inputs and outputs are stored as hashes; raw payloads are not part of the bundle. - Fields covered by certificateHash (whitelist)
- The hash is SHA-256 over a strict whitelist projection, canonicalized per the profile selected by
protocolVersion(1.2.0→ nexart-v1, default;1.3.0→ jcs-v1, RFC 8785, opt-in). Whitelist:bundleType,version,createdAt,snapshot, and (only when present)context,contextSummary,policyEvaluation. Any modification to a covered field changes the hash. Canonicalization is protocol-bound: verifiers MUST use the profile corresponding to the bundle'sprotocolVersion. - Fields excluded from hashing
- The following are explicitly excluded from the hash payload and may be added, updated, or removed without invalidating the
certificateHash:certificateHashitself,meta(includingmeta.attestationand the receipt),declaration,verificationEnvelope,verificationEnvelopeSignature,receipt, and any unknown fields not in the whitelist. Verifiers MUST apply the whitelist projection to the bundle as received; no reconstruction beyond the canonicalization profile bound to the bundle's protocolVersion. - The three verification layers
- certificateHash (integrity) — recompute SHA-256 over the JCS-canonicalized whitelist projection and compare with the bundle's
certificateHash. - receipt signature (node attestation) — validate the Ed25519 receipt at
meta.attestationusing the node key matched bykid, and confirm it references the bundle'scertificateHash. - verification envelope (full bundle signature, v0.16.1) — validate
meta.verificationEnvelopeSignatureagainstmeta.verificationEnvelope. When absent, this layer returns SKIPPED.
Each layer reports independently as PASS, FAIL, or SKIPPED. Verification statuses: VERIFIED, FAILED, NOT_FOUND.
- certificateHash (integrity) — recompute SHA-256 over the JCS-canonicalized whitelist projection and compare with the bundle's
- Independence model: local sealing, optional node certification, independent verification
Local sealing — produced by the SDK (
@nexart/ai-execution@1.0.0viasealCer()) or the CLI (@nexart/cli@1.0.0vianexart ai seal). Builds a canonical CER bundle and computes thecertificateHashfully offline. No API key, no network call, no receipt, no verification envelope. The result is a sealed bundle: integrity only.Optional node certification — performed by the attestation node (
POST /v1/cer/ai/certify/nexart ai certify). The node validates the bundle and issues an Ed25519-signed receipt (identified bykid) referencing the bundle'scertificateHash, plus a verification envelope. Receipt and signatures are stored atbundle.meta.attestationandmeta.verificationEnvelope. Certification adds attestation layers; it does not change thecertificateHash. The result is a certified bundle.Independent verification — performed by anyone, with no trust in NexArt infrastructure required. Available via verify.nexart.io, the SDK, or the CLI. The bundle plus the node's published public keys are sufficient. For sealed bundles, only Layer 1 (integrity) is applicable; Layers 2 and 3 return SKIPPED. For certified bundles, all three layers return PASS.
- Integrity ≠ Stamp ≠ Envelope.
- Verification does not require trust.
- Failure of one layer is not failure of all.
| Layer | Failure | Meaning | Action |
|---|---|---|---|
| Layer 1 — Integrity | certificateHash mismatch | The bundle was modified after sealing, or the wrong projection was hashed. | Re-derive certificateHash with sealCer(...) over the strict whitelist. If it still differs, the stored bundle is no longer authentic. |
| Layer 2 — Signed Receipt | Receipt signature invalid | The receipt was not produced by the published node key, or the receipt payload was modified. | Refetch the node key set from /.well-known/nexart-node.json. If signature still fails, the receipt is not trustworthy. |
| Layer 3 — Verification Envelope | Envelope signature invalid or projection mismatch | The envelope payload (5-field attestation projection) does not match what was signed, or the kid is wrong. | Confirm the bundle includes meta.verificationEnvelope and meta.verificationEnvelopeSignature. Public/redacted payloads cannot satisfy Layer 3. |
| Node | EXECUTION_MUTATION_DETECTED (409) | An execution_id already maps to a different certificateHash. The node rejects mutation by design. | Do not re-submit modified bundles under the same execution_id. Create a new execution. |
Local verification is not public verification.
verifyCer() andverifyProjectBundle() prove integrity only. To make an artifact resolvable onverify.nexart.io, you MUST register it on the attestation node. See theEnd-to-End Verification Flow.
How Verification Works
Verification confirms the integrity and authenticity of a Certified Execution Record or Project Bundle. The canonical flow is:
- An execution happens.
- A CER is sealed locally (SDK
sealCer()or CLInexart ai seal), binding execution metadata to a deterministiccertificateHash. This is fully offline. - The sealed bundle can be verified immediately. Layer 1 (integrity) returns PASS; Layers 2 and 3 return SKIPPED because no node attestation is present.
- Optionally, the CER is certified by an attestation node, which adds a signed receipt and a verification envelope.
- A certified bundle can be verified against all three layers (PASS/PASS/PASS) and is resolvable at verify.nexart.io.
- Optionally, multiple CERs are collected into a Project Bundle with a
projectHash.
Sealed vs Certified
| Property | Sealed (local) | Certified (node) |
|---|---|---|
| Origin | SDK / CLI, fully offline | Attestation node (POST /v1/cer/ai/certify) |
| API key required | no | yes (NEXART_API_KEY) |
| certificateHash | present | present (identical input → identical hash) |
| meta.attestation (receipt) | absent | present, Ed25519-signed |
| meta.verificationEnvelope | absent | present (v0.16.1) |
| Layer 1 — Integrity | PASS | PASS |
| Layer 2 — Receipt | SKIPPED | PASS |
| Layer 3 — Envelope | SKIPPED | PASS |
| Third-party verifiable | integrity only | yes (attested) |
SKIPPED is not a failure. A sealed bundle is a valid CER; it simply has not been attested by a node. Certification adds Layers 2 and 3 without changing thecertificateHash.
The Three Verification Layers
Verification is split into three independent layers. Each layer protects a distinct property and reports its result independently. Envelope failure MUST NOT be reported as integrity failure.
Layer 1 — Integrity (certificateHash)
Recomputes the certificateHash from the strict whitelist projection of the bundle, canonicalized per the profile selected by protocolVersion (1.2.0 → nexart-v1; 1.3.0 → jcs-v1 / RFC 8785). Proves the bundle has not been modified across covered fields.
Hashed fields: bundleType, version, createdAt, snapshot, context (if present), contextSummary (if present).
Not hashed: certificateHash, meta, declaration, verificationEnvelope, verificationEnvelopeSignature, receipt, any unknown fields.
Layer 2 — Receipt (Node stamp)
Validates the Ed25519 signed receipt over the certificateHash. Proves the node attested the bundle at a specific time. Receipt verification is independent of envelope verification.
Layer 3 — Verification Envelope (v0.16.1, v2)
Ed25519 signature over { attestation, bundle } using a strict whitelist projection. Proves full bundle integrity under the node's signature beyond the certificateHash.
- Attestation projection (5 fields, exact):
attestationId,attestedAt,kid,nodeRuntimeHash,protocolVersion. - Bundle projection:
bundleType,version,createdAt,snapshot,context(if present),contextSummary(if present). certificateHashis NOT part of the signed payload.metais NOT signed by the envelope.receiptis NOT signed by the envelope.
Envelope failure indicates the signed bundle projection has been altered. It is reported separately from Layer 1 and MUST NOT be conflated with integrity failure.
snapshot.input and snapshot.output are redacted. Therefore:- Full envelope verification is NOT possible from public data alone.
- Receipt verification IS possible.
- certificateHash verification IS possible (against the public-safe representation).
Why Envelope Verification May Fail
Envelope verification (Layer 3) is independent of integrity (Layer 1) and receipt (Layer 2). A bundle can be fully intact and properly attested while the envelope check returns FAIL or SKIPPED. The two outcomes carry different meaning and MUST NOT be conflated with integrity failure.
SKIPPED vs FAIL
- SKIPPED — the envelope check is not applicable. The bundle does not include
meta.verificationEnvelopeand/ormeta.verificationEnvelopeSignature, OR the verifier cannot reconstruct the signed projection (for example, fields required by the projection are not present in the received bundle). SKIPPED is NOT a failure. - FAIL — the envelope is present and reconstructable, but the Ed25519 signature does not validate against the projected payload using the node key matched by
kid.
Reasons Layer 3 returns FAIL while Layers 1 and 2 PASS
- Mutation of a signed-but-unhashed field. The envelope signs an attestation projection (
attestationId,attestedAt,kid,nodeRuntimeHash,protocolVersion) that is NOT covered bycertificateHash. Editing one of those fields breaks Layer 3 while Layer 1 still passes. - Re-serialization that breaks canonicalization. Layer 3 requires the exact canonicalization profile bound to the bundle's
protocolVersion(nexart-v1for 1.2.0,jcs-v1/ RFC 8785 for 1.3.0). Tools that pretty-print, reorder keys, change number formatting, or alter Unicode escaping invalidate the signature even when content is semantically identical. - Key mismatch (
kid). The verifier resolves the public key bykidfromnode.nexart.io/.well-known/nexart-node.json. If the published key set has rotated or thekidis unknown, the signature cannot be validated. - Partial / redacted bundle. Public-safe representations may strip fields from
snapshotor omit attestation projection fields. The envelope payload cannot be reconstructed bit-for-bit, so Layer 3 returns SKIPPED (or FAIL if the verifier attempts strict reconstruction). See "Why public bundles cannot always verify the envelope" below. - v0.16.0 signals payload alignment bug. Bundles created with
@nexart/ai-execution@1.0.0that include signals MAY fail envelope verification due to a payload alignment issue fixed inv0.16.1. Operators SHOULD re-certify affected executions viaPOST /v1/admin/recertify-batch. - Pre-envelope artifact. Bundles produced before v0.16.1 may not include any envelope at all. Layer 3 returns SKIPPED by compatibility fallback. This is NOT a failure of integrity or attestation.
Envelope FAIL does not imply Integrity FAIL
Because certificateHash and the envelope cover different projections (the envelope additionally signs the attestation projection and excludes certificateHash itself), a Layer 3 FAIL with Layer 1 PASS indicates the bundle's hashed content is intact, but the node's signed surface around it has been altered. Verifiers MUST report each layer independently. The aggregate verification status follows the rules in Verification Statuses and Errors.
Why public bundles cannot always verify the envelope
The public verifier consumes a redacted representation of the record. Raw snapshot.input and snapshot.output are never exposed publicly; only their SHA-256 digests are. Some metadata fields may also be withheld depending on export settings. Because Layer 3 signs a strict whitelist projection that includes snapshot (and, when present, context / contextSummary), a verifier without the exact source bytes cannot reconstruct the signed payload byte-for-byte.
In that case the public verifier returns SKIPPED for Layer 3 and still reports PASS for Layer 1 (against the public-safe representation) and Layer 2 (receipt validation against certificateHash). Full envelope verification requires the original, non-redacted CER bundle — typically held by the producer or an authorized auditor.
What Verification Proves
Verification answers up to four questions about a Certified Execution Record:
- Has the CER bundle been modified since it was created?
- Was the receipt signed by a valid NexArt attestation node?
- Does the receipt reference the correct
certificateHash? - Has the verification envelope been altered? (when present)
If all applicable checks pass, the record is intact and its attestation is trustworthy. For a detailed breakdown, see AI CER Verification Layers.
@nexart/ai-execution@1.0.0 that include signals may fail envelope verification due to a payload alignment issue. v0.16.1 fixes this. Re-certification may be required. Operators can use POST /v1/admin/recertify-batch to re-seal affected executions.How to Verify a Record
There are three ways to verify a CER through the public verifier at verify.nexart.io. Prefer certificateHash for any persisted reference — it is the canonical identity of the artifact. executionId is convenience metadata only.
1. By Certificate Hash (canonical)
If you have the certificateHash, use the hash-based URL. The colon in the hash must be URL-encoded:
https://verify.nexart.io/c/sha256%3A7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d90692. By Execution ID (convenience only)
executionId is a convenience identifier returned by the certify API. It MUST NOT be used as a primary key for storage or deduplication. Use it for ad-hoc lookup only:
https://verify.nexart.io/e/exec_abc1233. By Uploading a CER Bundle
You can also verify a record by uploading or pasting the full CER JSON bundle at verify.nexart.io. The verifier will recompute the hash and check the signature locally.
This is useful when you have the CER file but not the execution ID or verification URL.
What Is Publicly Visible?
The public verifier uses a redacted, public-safe representation of the record:
- Verification status, certificateHash, timestamp, and node identity are visible.
- Raw input and output content is never exposed. The record contains SHA-256 hashes of these fields, not the original text.
- Metadata fields (like appId) may be included or redacted depending on the record's export settings.
Anyone can verify that a record is intact and properly signed without seeing the private data that produced it.
What Gets Stored for Public Verification?
When a record is certified:
- The attestation node returns a signed receipt containing the certificateHash, timestamp, node identity, and Ed25519 signature. This is stored at
meta.attestationin the CER bundle. - A redacted, public-safe version of the record is persisted for verification.
- Input and output content is hashed (SHA-256). The hashes appear in the record, but the original content is not stored by the node or verifier.
- You control which metadata fields are included when you create the record.
Verification Checks
Each check returns PASS, FAIL, or SKIPPED:
- Bundle Integrity. Recompute the
certificateHashfrom the bundle contents. If the hash differs, the bundle has been modified. - Node Signature. Validate the Ed25519 signature using the public key published at
node.nexart.io/.well-known/nexart-node.json, matched by thekidin the receipt. SKIPPED if no attestation is present. - Receipt Consistency. Confirm the receipt at
meta.attestation.receiptreferences the samecertificateHashas the bundle and that the node identity matches. SKIPPED if no attestation is present. - Verification Envelope. When present, validate
meta.verificationEnvelopeSignatureagainstmeta.verificationEnvelope. This confirms the authoritative displayed verification surface has not been altered. SKIPPED if the bundle does not include a verification envelope.
For a detailed breakdown of what each layer protects, see AI CER Verification Layers.
Verification Statuses
The verification status reflects the overall outcome, as defined by the CER Protocol:
VERIFIED All applicable checks pass. The CER is intact and,
if attested, has a valid signed receipt.
FAILED One or more checks fail. The record may have been
modified or the signature does not match.
NOT_FOUND The requested execution record was not located.Reading Verification Results
The verifier reports four distinct outcome classes. They are not all failures:
- VERIFIED: all applicable checks pass.
- VERIFIED (supplemental): core integrity passes, but supplemental context (e.g. signals outside the hash scope) is present. This is NOT a failure; supplemental fields are simply not cryptographically bound.
- FAILED: one or more applicable checks fail.
- NOT_FOUND: the artifact was never registered on the node. See End-to-End Verification.
For the precise semantics of each result class, reseal handling, and the canonical role of certificateHash vs executionId, see Verification Semantics.
Expected Outcomes by Bundle Type
- cer.ai.execution.v1 (with attestation): all checks PASS → VERIFIED
- cer.ai.execution.v1 (without attestation): bundleIntegrity PASS, attestation checks SKIPPED → VERIFIED
- signed-redacted-reseal: redacted reseal returned by the public verifier; validated against the NEW resealed
certificateHash→ VERIFIED - hash-only-timestamp: only certificateHash is attested → VERIFIED
- legacy: older format, limited coverage → VERIFIED or FAILED depending on data
Project Bundle Verification
For multi-step workflows, verification covers the entire Project Bundle:
- projectHash integrity: recomputed from canonical content using
sha256-canonical-json - Per-step CER verification: each embedded CER's
certificateHashis checked independently - Project-level node receipt: validated if present
Use verifyProjectBundle() (Node) or verifyProjectBundleAsync() (browser) from the SDK, or verify at verify.nexart.io via /p/:projectHash.
Independent Verification (No API Required)
You can verify a CER without calling any NexArt API. No trust in NexArt infrastructure is required. All you need is the CER bundle (including meta.attestation) and access to the node's published keys:
- Recompute the
certificateHashfrom the CER bundle (SHA-256 over the JCS-canonicalized whitelist projection) - Compare it with the
certificateHashinmeta.attestation.receipt - Fetch the node's public key from
node.nexart.io/.well-known/nexart-node.json - Find the key matching the receipt's
kid - Verify the Ed25519 signature over the receipt payload
If all steps pass, you can trust the attestation independently of NexArt infrastructure. No account, API key, or network call to NexArt is required beyond fetching the node's public key. For a from-scratch implementation guide in any language, see External Verification.
protocolVersion and Canonicalization
Every attested bundle declares its canonicalization profile in meta.attestation.protocolVersion. Verifiers MUST select the profile by this field and MUST fail closed on unknown values.
protocolVersion = "1.2.0" profile = "nexart-v1" default (custom canonicalization)
protocolVersion = "1.3.0" profile = "jcs-v1" opt-in (RFC 8785, standards-based)Canonicalisation is protocol-bound. There is no universal default. Verifiers MUST use the canonicalisation corresponding to the bundle's protocolVersion or hash recomputation will fail. Do NOT assume RFC 8785 universally.
1.2.0 → nexart-v1 is the current default canonicalization profile. All SDK and CLI calls produce 1.2.0 bundles unless the producer explicitly opts into a different version.
1.3.0 → jcs-v1 is opt-in. It uses JSON Canonicalization Scheme (RFC 8785) for both hashing and signing, providing a published, standards-based deterministic byte representation. Producers select it via createSnapshot({ protocolVersion: "1.3.0" }) or the CLI flag --protocol-version 1.3.0.
Both profiles are accepted indefinitely. Verifiers MUST support both to remain compliant.protocolVersion is part of the attestation projection signed by the verification envelope, so it cannot be silently retargeted to a different profile without breaking Layer 3.
Fail-closed Behavior
Verification MUST fail closed. Any condition where a verifier cannot determine the correct rule to apply is reported as FAILED, never silently passed. SKIPPED is reserved for layers that are not applicable to the bundle as received (for example, an unsigned sealed bundle has no Layer 2 to run).
- Unknown
protocolVersion→ FAILED. - Unknown
bundleType→ FAILED. - Receipt
kidnot present in the published node key set → FAILED (Layer 2). - Envelope present but a required projection field is missing or cannot be canonicalized → FAILED (Layer 3).
- Envelope absent → SKIPPED (Layer 3). Not a failure; the bundle simply does not carry that proof.
What Verification Does NOT Guarantee
A PASS result asserts exactly what the protocol attests, and nothing more. The following properties are explicitly out of scope:
- Completeness. Verification does not prove that the bundle represents every step of a larger workflow. Whether all relevant executions were captured is an integration concern.
- Semantic correctness. NexArt proves the bundle was produced as recorded. It does not assert that the model output was correct, appropriate, or compliant.
- Trusted (third-party) timestamping.
attestedAtis a node-issued timestamp, bound to the bundle'scertificateHashby the receipt signature. It provides an ordering guarantee relative to other records signed by the same node; it is NOT independent proof of existence at that wall-clock time. An external TSA (e.g. RFC 3161 via DigiCert) MAY be integrated alongside the receipt without modifying existing records. - Public log inclusion. The current attestation log is node-internal. There is no public Merkle tree and no external witness at this protocol version. Verifiers MUST NOT infer transparency-log inclusion from a PASS result.
- Deterministic replay. Recomputing the same model output from the same inputs is only meaningful in controlled environments (fixed model version, seed, temperature 0, deterministic decoding). NexArt attests the recorded I/O; it does not assert reproducibility of the upstream model.
CLI Usage
@nexart/cli@1.0.0 provides a verifier that runs offline against a CER bundle file. It fetches the node key set only when the bundle declares an attestation.
npm install -g @nexart/cli@1.0.0nexart ai verify ./cer.jsoncertificateHash : sha256:7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069
protocolVersion : 1.2.0 (profile: nexart-v1)
Integrity (L1) : PASS
Receipt (L2) : PASS
Envelope (L3) : PASS
status : VERIFIEDcertificateHash : sha256:9f2b1c8e4a7d6f3b0c5e8a1d2f4b6c8e9a0d3f5b7c2e4a6d8f1b3c5e7a9d0f2b
protocolVersion : 1.2.0 (profile: nexart-v1)
Integrity (L1) : PASS
Receipt (L2) : SKIPPED (no attestation present)
Envelope (L3) : SKIPPED (no envelope present)
status : VERIFIEDexit 0 status = VERIFIED
exit 1 status = FAILED (any applicable layer failed)
exit 2 status = NOT_FOUND (referenced record could not be located)
exit 3 usage error (missing file, malformed JSON, unknown flag)
Failures print a machine-readable JSON report to stderr:
{ "status": "FAILED", "checks": { "bundleIntegrity": "FAIL", ... }, "reason": "..." }# Default is 1.2.0 (nexart-v1). Opt into 1.3.0 (jcs-v1, RFC 8785):
npx @nexart/cli@1.0.0 ai seal input.json --protocol-version 1.3.0 --out cer.json
npx @nexart/cli@1.0.0 ai verify cer.json
# --protocol-version overrides the producer default for this invocation only.
# Verifiers select the canonicalization profile from the bundle's protocolVersion;
# the flag does not change verification behavior.For the full verification contract, see the CER Protocol specification. For SDK functions, see AI Execution SDK (sync and async modes). For browser-specific usage, see Browser Verification.