nexart.iodocs

    AI Execution SDK

    API reference for certifying AI executions, creating CER bundles, and working with CER packages.

    All SDK helpers must follow this flow.

    NexArt Integration (Canonical Flow)

    Flow:

    1. Capture execution input
    2. Seal locally (offline)
    3. Verify locally
    4. (Optional) Certify via node
    5. 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", a version, a createdAt ISO-8601 timestamp, a snapshot (model, inputHash, outputHash, metadata), and an optional context / contextSummary. The CER is identified by its certificateHash (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.0nexart-v1, default; 1.3.0jcs-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's protocolVersion.
    Fields excluded from hashing
    The following are explicitly excluded from the hash payload and may be added, updated, or removed without invalidating the certificateHash: certificateHash itself, meta (including meta.attestation and 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.attestation using the node key matched by kid, and confirm it references the bundle's certificateHash.
    • verification envelope (full bundle signature, v0.16.1) — validate meta.verificationEnvelopeSignature against meta.verificationEnvelope. When absent, this layer returns SKIPPED.

    Each layer reports independently as PASS, FAIL, or SKIPPED. Verification statuses: VERIFIED, FAILED, NOT_FOUND.

    Independence model: local sealing, optional node certification, independent verification

    Local sealing — produced by the SDK (@nexart/ai-execution@1.0.0 via sealCer()) or the CLI (@nexart/cli@1.0.0 via nexart ai seal). Builds a canonical CER bundle and computes the certificateHash fully 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 by kid) referencing the bundle's certificateHash, plus a verification envelope. Receipt and signatures are stored at bundle.meta.attestation and meta.verificationEnvelope. Certification adds attestation layers; it does not change the certificateHash. 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.

    Overview

    The AI Execution SDK provides functions for sealing CERs locally and for certifying them via the attestation node. The canonical workflow is: seal → verify → (optional) certify → verify. Sealing is offline and requires no API key. Certification adds independently verifiable node attestation without changing the certificateHash.

    Framework-agnostic primitives (use these by default)
    • certifyDecision(params) — sync seal. No network.
    • certifyAndAttestDecision(params, options) — async seal + attest. Returns { bundle, receipt }.
    • attest(bundle, options) — async. Attest a previously sealed bundle.
    • Required CertifyDecisionParams: provider, model, prompt, input, parameters, output.

    Sync/async asymmetry: certifyDecision in @nexart/ai-execution is synchronous. certifyDecision exposed by @nexart/agent-kit is asynchronous (dispatches through the workflow runner). The two are not interchangeable.

    LangChain-specific adapters (createLangChainCer, certifyLangChainRun) live in @nexart/ai-execution/langchain and are appropriate only when inputs are already shaped as LangChain Run / callback payloads. See the LangChain integration page.

    Mental model
    • Logs describe. CERs prove.
    • Integrity ≠ Stamp ≠ Envelope.
    • Verification does not require trust.

    Quick Implementation Flow

    Capture, seal, verify, optionally certify, verify again. The SDK exposes one function per step.

    Quick Implementation Flow
    The four steps required to integrate NexArt correctly. Each step has a single responsibility.
    1. Step 1Capture execution
      What: Record provider, model, prompt, input, parameters, and output. All six are required on CertifyDecisionParams.
      Why: The certificateHash is computed from this data. Capture must happen before sealing.
      @nexart/signals · createContext() (optional) → passed into certifyDecision(...)
    2. Step 2Create CER
      What: Seal a CER bundle and compute certificateHash over the strict whitelist (JCS).
      Why: The hash is the canonical identity of the record. It binds the bundle to its content.
      certifyDecision(params) · or sealCer(snapshot) for the lower-level path
    3. Step 3Certify via node
      What: Submit the bundle to the attestation node. Receive a signed receipt and verification envelope.
      Why: The node provides an independent witness and a public verification surface.
      certifyAndAttestDecision(params, options) · or attest(bundle, options) · POST /v1/cer/ai/certify
    4. Step 4Verify independently
      What: Anyone can re-derive certificateHash, validate the receipt signature, and validate the envelope.
      Why: Verification requires no trust in your infrastructure or the node beyond its published key.
      verifyAiCerBundleDetailed(bundle) · or https://verify.nexart.io/c/{bundle.certificateHash}
    Deeper reference: Verification · Verification Layers · Hashes

    Minimal working example

    The canonical example used across the docs. Use this as the starting point for any integration.

    How the SDK is split
    • Creation is local and deterministic (certifyDecision). It produces a sealed bundle and a certificateHash. No network call.
    • Certification is node-based (certifyAndAttestDecision or attest(bundle, options)). It returns an AttestationReceipt and the bundle gains a verification envelope.
    • A public verification URL exists ONLY after certification. Construct it as https://verify.nexart.io/c/{certificateHash}.
    • prompt and parameters are REQUIRED fields on CertifyDecisionParams, alongside provider, model, input, and output.
    Flow A - Certified execution (recommended)
    import {
      certifyAndAttestDecision,
      verifyAiCerBundleDetailed,
    } from "@nexart/ai-execution";
    
    // Steps 1-3: capture execution, seal the CER, attest via the node.
    // certifyAndAttestDecision is async because it contacts the attestation node.
    const { bundle, receipt } = await certifyAndAttestDecision(
      {
        provider:   "openai",
        model:      "gpt-4o-mini",
        prompt:     "Should this refund be approved?",
        input:      { messages: [{ role: "user", content: "Should this refund be approved?" }] },
        parameters: { temperature: 0, maxTokens: 1024, topP: null, seed: null },
        output:     { decision: "approve", reason: "policy_passed" },
      },
      {
        nodeUrl: process.env.NEXART_NODE_ENDPOINT!,
        apiKey:  process.env.NEXART_API_KEY!,
      },
    );
    
    const certificateHash = bundle.certificateHash;
    const verificationUrl = `https://verify.nexart.io/c/${certificateHash}`;
    
    console.log(certificateHash);         // sha256:...
    console.log(receipt.attestationId);   // node-issued id
    console.log(verificationUrl);         // https://verify.nexart.io/c/sha256:...
    
    // Step 4: independent verification. No trust in your infrastructure required.
    const report = await verifyAiCerBundleDetailed(bundle);
    // report.checks.bundleIntegrity    -> PASS  (Layer 1)
    // report.checks.nodeSignature      -> PASS  (Layer 2)
    // report.checks.receiptConsistency -> PASS  (Layer 3)
    
    Flow B - Local-only execution (no node interaction)
    import { certifyDecision, verifyAiCerBundleDetailed } from "@nexart/ai-execution";
    
    // certifyDecision is synchronous and deterministic.
    // It produces a sealed CER bundle and certificateHash WITHOUT contacting the node.
    // There is no receipt, no envelope, and no public verificationUrl in this flow.
    const bundle = certifyDecision({
      provider:   "openai",
      model:      "gpt-4o-mini",
      prompt:     "Should this refund be approved?",
      input:      { messages: [{ role: "user", content: "Should this refund be approved?" }] },
      parameters: { temperature: 0, maxTokens: 1024, topP: null, seed: null },
      output:     { decision: "approve", reason: "policy_passed" },
    });
    
    const certificateHash = bundle.certificateHash;
    console.log(certificateHash);   // sha256:...
    
    // Local verification: Layer 1 PASS, Layers 2 & 3 SKIPPED.
    const report = await verifyAiCerBundleDetailed(bundle);
    
    // To obtain a signed receipt and a public verificationUrl, attest the bundle
    // via certifyAndAttestDecision(...) or attest(bundle, options).
    

    Using LangChain? See the dedicated LangChain integration page for the optional adapters createLangChainCer / certifyLangChainRun imported from @nexart/ai-execution/langchain.

    Copy-Paste Test Harness

    Single-file script that exercises the full SDK flow end-to-end and prints PASS / FAIL per verification layer. Use it to validate an integration in under two minutes without mixing APIs or guessing at parameters.

    Verify Your First Certified Execution (2 minutes)
    Copy this script, set two environment variables, run it. If you see three PASS lines, NexArt is working end-to-end. No abstractions, no helpers, no partial snippets.
    1. Install
    npm install @nexart/ai-execution
    export NEXART_NODE_ENDPOINT="https://node.nexart.io"
    export NEXART_API_KEY="<your-api-key>"
    2. test-harness.ts (single file, copy as-is)
    import {
      certifyAndAttestDecision,
      verifyAiCerBundleDetailed,
    } from "@nexart/ai-execution";
    
    async function main() {
      // Seal + attest in one node round-trip.
      const { bundle, receipt } = await certifyAndAttestDecision(
        {
          provider:   "openai",
          model:      "gpt-4o-mini",
          prompt:     "Should this refund be approved?",
          input:      { messages: [{ role: "user", content: "Should this refund be approved?" }] },
          parameters: { temperature: 0, maxTokens: 1024, topP: null, seed: null },
          output:     { decision: "approve", reason: "policy_passed" },
        },
        {
          nodeUrl: process.env.NEXART_NODE_ENDPOINT!,
          apiKey:  process.env.NEXART_API_KEY!,
        },
      );
    
      const certificateHash = bundle.certificateHash;
      const verificationUrl = `https://verify.nexart.io/c/${certificateHash}`;
    
      console.log("certificateHash :", certificateHash);
      console.log("attestationId   :", receipt.attestationId);
      console.log("verificationUrl :", verificationUrl);
    
      // Independent verification of the returned bundle. No trust required.
      const report = await verifyAiCerBundleDetailed(bundle);
    
      console.log("Integrity (Layer 1) :", report.checks.bundleIntegrity);
      console.log("Receipt   (Layer 2) :", report.checks.nodeSignature);
      console.log("Envelope  (Layer 3) :", report.checks.receiptConsistency);
    }
    
    main().catch((err) => {
      console.error("FAILED:", err);
      process.exit(1);
    });
    3. Run
    npx tsx test-harness.ts
    Expected output (success)
    certificateHash : sha256:9f2b1c8e4a7d6f3b0c5e8a1d2f4b6c8e9a0d3f5b7c2e4a6d8f1b3c5e7a9d0f2b
    verificationUrl : https://verify.nexart.io/c/sha256:9f2b1c8e4a7d6f3b0c5e8a1d2f4b6c8e9a0d3f5b7c2e4a6d8f1b3c5e7a9d0f2b
    Integrity (Layer 1) : PASS
    Receipt   (Layer 2) : PASS
    Envelope  (Layer 3) : PASS

    Three PASS lines mean the bundle is byte-identical to what the node attested, the receipt signature validates against the node key, and the verification envelope binds the attestation projection to the bundle.

    Expected verification result — Sealed bundle (local, offline)
    integrity
    : PASS
    receipt
    : SKIPPED
    envelope
    : SKIPPED

    Produced by nexart ai seal or the SDKsealCer(). No node call, no API key.

    Expected verification result — Certified bundle (node-attested)
    integrity
    : PASS
    receipt
    : PASS
    envelope
    : PASS

    Produced by nexart ai certify or the SDKcertifyAndAttestDecision(). Node returns receipt + envelope.

    If any layer returns FAIL, the integration is incorrect. SKIPPED is expected for local (sealed) artifacts and MUST NOT be treated as a failure.

    If something fails
    • Integrity FAIL (Layer 1)
      Payload mismatch. The recomputed certificateHash does not match the bundle. Cause: the bundle was mutated, re-serialized with a different canonicalization, or the version field was changed. The bundle MUST be persisted byte-for-byte after certification.
    • Receipt FAIL (Layer 2)
      Node or auth issue. The receipt signature did not validate against the node key. Cause: wrong NEXART_NODE_ENDPOINT, missing or invalid NEXART_API_KEY, or the node key published at /.well-known/nexart-node.json does not match the receipt kid.
    • Envelope FAIL (Layer 3)
      Bundle mutation after attestation. The envelope signature covers a 5-field attestation projection (attestationId, attestedAt, kid, nodeRuntimeHash, protocolVersion). If any of those fields were altered or stripped, the envelope cannot validate. Do not modify meta.attestation after sealing.
    Recommended for most integrations
    Use POST /v1/cer/ai/certify to create a CER, get node attestation, and receive a verification URL in one call.

    API Endpoints

    POST /v1/cer/ai/certify

    Creates a Certified Execution Record, attests it through the node, and returns a signed receipt with a verification URL. This is the simplest way to get a verifiable proof of an AI execution.

    What it does:

    • Creates a CER bundle with execution metadata
    • Computes the certificateHash (SHA-256)
    • Submits the bundle to the attestation node
    • Returns the signed receipt, signature, and a public verification URL
    Certify Request
    POST /v1/cer/ai/certify
    Authorization: Bearer NEXART_API_KEY
    
    {
      "model": "gpt-4",
      "input": "Summarize this contract...",
      "output": "The contract states that...",
      "metadata": {
        "appId": "my-app",
        "projectId": "proj_abc123"
      }
    }
    Certify Response
    {
      "verificationUrl": "https://verify.nexart.io/e/exec_abc123",
      "certificateHash": "sha256:9e8d7c6b5a4f3210...",
      "receipt": {
        "certificateHash": "sha256:9e8d7c6b5a4f3210...",
        "timestamp": "2026-03-06T12:00:01.000Z",
        "nodeId": "nexart-node-primary",
        "kid": "key_01HXYZ..."
      },
      "signatureB64Url": "MEUCIQD3a8b1c4d5e6f..."
    }

    The API response includes receipt and signature fields for convenience. The canonical location for attestation data within the CER bundle is bundle.meta.attestation.

    POST /v1/cer/ai/create

    Creates a CER bundle without attestation. The bundle is returned with a certificateHash, but no receipt or verification URL is generated.

    When to use this:

    • You want to generate the CER bundle for your own records
    • You plan to submit the bundle for attestation separately
    • You need the bundle structure but do not need node signing right now
    Create Request
    POST /v1/cer/ai/create
    Authorization: Bearer NEXART_API_KEY
    
    {
      "model": "gpt-4",
      "input": "Summarize this contract...",
      "output": "The contract states that...",
      "metadata": {
        "appId": "my-app",
        "projectId": "proj_abc123"
      }
    }
    Create Response
    {
      "bundleType": "cer.ai.execution.v1",
      "version": "0.1",
      "createdAt": "2026-03-06T12:00:00.000Z",
      "snapshot": {
        "model": "gpt-4",
        "inputHash": "sha256:a1b2c3d4e5f67890...",
        "outputHash": "sha256:f6e5d4c3b2a10987...",
        "metadata": {
          "appId": "my-app",
          "projectId": "proj_abc123"
        }
      },
      "certificateHash": "sha256:9e8d7c6b5a4f3210..."
    }

    Note: the create response returns the CER bundle directly. There is no receipt, signature, or verification URL. The meta.attestation field is absent. To get attestation, use /certify instead.

    Authentication

    Both endpoints require an API key passed as a Bearer token:

    Authorization: Bearer NEXART_API_KEY

    CER Bundle Shape

    A fully certified CER bundle follows this structure:

    CER Bundle (Certified)
    {
      "bundleType": "cer.ai.execution.v1",
      "version": "0.1",
      "createdAt": "2026-03-06T12:00:00.000Z",
      "snapshot": {
        "model": "gpt-4",
        "inputHash": "sha256:a1b2c3d4e5f67890...",
        "outputHash": "sha256:f6e5d4c3b2a10987...",
        "metadata": {
          "appId": "my-app",
          "projectId": "proj_abc123"
        }
      },
      "certificateHash": "sha256:9e8d7c6b5a4f3210...",
      "meta": {
        "attestation": {
          "receipt": {
            "certificateHash": "sha256:9e8d7c6b5a4f3210...",
            "timestamp": "2026-03-06T12:00:01.000Z",
            "nodeId": "nexart-node-primary",
            "kid": "key_01HXYZ..."
          },
          "signature": "<raw Ed25519 signature bytes>",
          "kid": "key_01HXYZ..."
        }
      }
    }

    The snapshot contains execution metadata. Input and output content is hashed (SHA-256) rather than stored directly. Attestation data lives under meta.attestation.

    Key Terms

    • create vs certify. create produces a CER bundle. certify produces a CER bundle and gets it attested by the node in one step.
    • Attestation vs verification. Attestation is when the node signs a record. Verification is when anyone checks that the signature and bundle are valid.
    • Signed receipt vs hash-only timestamp. A signed receipt attests the full CER bundle. A hash-only timestamp attests only the certificateHash, without the snapshot contents.

    Context Signals

    Both endpoints accept an optional signals parameter - an array of structured metadata objects recorded alongside the execution.

    Request with Signals
    {
      "model": "gpt-4",
      "input": "Summarize this contract...",
      "output": "The contract states that...",
      "signals": [
        {
          "type": "policy.check",
          "source": "compliance-engine",
          "payload": { "policyId": "ret-30d", "result": "pass" }
        }
      ]
    }

    When signals are present, they are included in the certificateHash computation. This makes them part of the tamper-evidence chain - any modification to a signal after certification will cause verification to fail.

    Signals are backward-compatible. Requests without the signals field behave exactly as before. Existing CER bundles without signals remain valid and verifiable.

    For the builder API to capture structured signals (input, tool calls, decisions, output) using @nexart/signals, see the Signals SDK reference and Execution Context and Signals.

    Verification

    As of v0.16.1, the SDK provides both sync and async verification functions:

    ModeEnvironmentFunctions
    SyncNode 18+verifyProjectBundle(), verifyCer()
    AsyncBrowser, Edge, Node 18+verifyProjectBundleAsync(), verifyCerAsync(), verifySnapshotAsync()

    You no longer need to write your own browser verifier. The async functions use the Web Crypto API internally.

    CERs produced by the certify endpoint can be verified across multiple layers:

    • Bundle Integrity. The CER bundle hashes are internally consistent.
    • Node Signature. The receipt signature is valid against the node's published Ed25519 key.
    • Receipt Consistency. The receipt's certificateHash matches the CER bundle.
    • Verification Envelope. When present, meta.verificationEnvelopeSignature validates the authoritative displayed verification surface.

    Verification statuses: VERIFIED, FAILED, or NOT_FOUND. Each check returns PASS, FAIL, or SKIPPED.

    Verify at verify.nexart.io or locally using the bundle and node keys from node.nexart.io/.well-known/nexart-node.json. For a full breakdown of verification layers, see AI CER Verification Layers. For browser examples, see Browser Verification.

    CER Package Helpers

    As of @nexart/ai-execution@1.0.0, the SDK includes official helpers for working with the CER package format. A CER package is a transport/export wrapper around the inner cer bundle. The inner cer remains the actual execution artifact.

    API

    • isCerPackage(value) - type guard; returns true if the value conforms to the CER package shape
    • createCerPackage(params) - creates a CER package from a bundle, receipt, signature, and optional verification artifacts
    • getCerFromPackage(pkg) - extracts the inner cer bundle from a package
    • exportCerPackage(pkg) - serializes a CER package to JSON
    • importCerPackage(json) - deserializes and validates a CER package from JSON
    • verifyCerPackage(pkg) - verifies the inner CER bundle within a package
    Scope of verifyCerPackage()
    verifyCerPackage() verifies the inner CER bundle (hash integrity, node signature, receipt consistency). It does not fully verify all package-level trust layers such as the verification envelope. For full package-level verification, use the verification layers documented in the AI CER Verification Layers spec.

    Creating a CER Package

    Create and export a CER package
    import { createCerPackage, exportCerPackage } from "@nexart/ai-execution";
    
    const pkg = createCerPackage({
      cer: bundle,
      receipt,
      signature: signatureB64Url,
      attestation: { nodeId, kid, timestamp }
    });
    
    const json = exportCerPackage(pkg);
    // json is a serialized CER package ready for storage or transport

    Importing and Verifying a CER Package

    Import and verify a CER package
    import {
      importCerPackage,
      getCerFromPackage,
      verifyCerPackage
    } from "@nexart/ai-execution";
    
    const pkg = importCerPackage(jsonString);
    const cer = getCerFromPackage(pkg);
    
    const result = await verifyCerPackage(pkg);
    // result contains inner CER verification status
    // Package-level trust layers (e.g. verification envelope) are not fully verified by this helper

    Package helpers are additive only. They do not change CER hashing, attestation, or verification semantics. See the AI CER Package Format specification for the normative package structure.

    Additional Exports

    The SDK exports a number of helpers for advanced or operator-level workflows. Importable from @nexart/ai-execution.

    verifyAiCerBundleDetailed(bundle)

    Returns a structured per-layer verification report covering Integrity (Layer 1), Receipt (Layer 2), and Verification Envelope (Layer 3). Each layer reports independently. Envelope failure is reported separately from integrity failure.

    sealCer(snapshot, options?)

    Lower-level seal. Takes a pre-built AiExecutionSnapshotV1 (not the high-level CertifyDecisionParams) and computes the certificateHash over the strict whitelist projection (bundleType, version, createdAt, snapshot, context, contextSummary, policyEvaluation) using the canonicalization profile bound to snapshot.protocolVersion (1.2.0nexart-v1 by default; 1.3.0jcs-v1 / RFC 8785 when explicitly set). For most callers, certifyDecision(params) is the recommended entry point.

    Opting into protocolVersion 1.3.0 (jcs-v1, RFC 8785)
    import { createSnapshot, sealCer } from "@nexart/ai-execution";
    
    const snapshot = createSnapshot({
      // protocolVersion MUST be set at creation time. Default is "1.2.0"
      // (nexart-v1) for backward compatibility. Set "1.3.0" explicitly to
      // use RFC 8785 JCS canonicalization for the certificateHash.
      protocolVersion: "1.3.0",
      model: "gpt-4o",
      input,
      output,
    });
    
    const cer = sealCer(snapshot);
    // cer.snapshot.protocolVersion              === "1.3.0"
    // cer.meta.attestation.protocolVersion      === "1.3.0" (mirrored by the node)
    // verificationEnvelope attestation.protocolVersion === "1.3.0"
    //
    // Verifiers MUST canonicalize with jcs-v1 for this bundle.
    // Do NOT mutate protocolVersion after sealing — it is inside the
    // whitelist projection that produces the certificateHash.
    Callout. If protocolVersion is omitted or null, NexArt defaults to 1.2.0 (nexart-v1) for backward compatibility. RFC 8785 JCS canonicalization is used only when you explicitly pass "1.3.0" at creation/sealing/certification time. Unsupported explicit values (for example "2.0.0") are rejected by the node before signing and fail closed at verification.

    wrapProvider(provider, opts)

    Wraps an LLM/tool provider so each invocation is automatically certified through /v1/cer/ai/certify. Returns the original provider response augmented with the CER bundle and verificationUrl.

    redactBeforeSeal(bundle, fields)

    Removes specified fields from the snapshot prior to computing certificateHash. Use to keep sensitive content out of the hashed payload while preserving verifiability over the redacted bundle.

    sanitizeForAttestation(bundle)

    Returns the strict whitelist projection of a bundle suitable for attestation submission. Strips fields outside the certificateHash whitelist.

    sanitizeForStorage(bundle)

    Returns a representation safe to store at rest. Preserves all fields required for verification while applying configured redactions.

    Canonicalization helpers

    • canonicalJson(value, profile?) — protocol-bound canonicalization. profile is selected from the bundle's protocolVersion: nexart-v1 for 1.2.0 (default), jcs-v1 (RFC 8785) for 1.3.0 (opt-in).
    • hashCanonicalJson(value, profile?) — SHA-256 over the canonicalized serialization. Used internally by sealCer.
    • deriveSignablePayload(bundle, attestation) — produces the derived canonical payload that the node Ed25519-signs. The signed payload is NOT the raw bundle.

    Project Bundle helpers

    • createProjectBundle(steps, metadata) — assembles a Project Bundle from step CERs.
    • verifyProjectBundle(bundle) / verifyProjectBundleAsync(bundle) — verify a Project Bundle (sync/async).
    • computeProjectHash(bundle) — recompute projectHash from canonical step content.

    Agent Kit

    For agent workflows, @nexart/agent-kit provides wrapTool() and certifyDecision() as thin convenience wrappers over this SDK.

    Common mistakes

    Common Mistakes
    Verified failure patterns observed in real integrations. Avoid these to prevent verification errors.
    • Wrong: Setting version: "1.0" on the bundle
      Right: Use version: "0.1" (matches cer.ai.execution.v1).
      Why: An incorrect version changes the canonical projection and breaks certificateHash recomputation.
    • Wrong: Hashing the full bundle (including meta.attestation, signature, receipt)
      Right: Hash only the strict whitelist: bundleType, version, createdAt, snapshot, context, contextSummary (JCS).
      Why: meta and attestation are added after sealing. Hashing them produces a different, non-verifiable hash.
    • Wrong: Trying to verify the Verification Envelope from a publicly redacted record
      Right: Layer 3 envelope verification requires the full attestation projection. Redacted public payloads support Layer 1 and Layer 2 only.
      Why: Envelope signature covers a 5-field projection that may be removed by redaction.
    • Wrong: Mutating the bundle after certification (re-ordering keys, adding fields, normalizing dates)
      Right: Treat the certified bundle as immutable. Persist it byte-for-byte.
      Why: Any mutation invalidates certificateHash and the receipt. The node enforces EXECUTION_MUTATION_DETECTED (409) on resubmit.
    • Wrong: Looking up records by executionId
      Right: Always look up by certificateHash. executionId is not a unique artifact identity.
      Why: Two attempts of the same execution can share an executionId but produce different certificateHashes.

    What happens if it fails

    What happens if verification fails
    Each verification layer fails for a different, specific reason. Isolating the failed layer tells you exactly what went wrong.
    LayerFailureMeaningAction
    Layer 1 — IntegritycertificateHash mismatchThe 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 ReceiptReceipt signature invalidThe 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 EnvelopeEnvelope signature invalid or projection mismatchThe 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.
    NodeEXECUTION_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.

    Scope

    The SDK API surface is still evolving. This page documents the current endpoints and data model. Check back for updates as new capabilities are added.