AI Execution Integrity

    Tamper-evident records for AI and LLM executions.

    Surface: ai.execution.v1CER: cer.ai.execution.v2Protocol: v1.2.0 (default) · v1.3.0 (RFC 8785, opt-in) · v1.3.1 (confidential)SDK: @nexart/ai-execution v1.4.0

    Minimum Integration, 3 Steps

    1. Create a CER bundle, certifyDecision() or createSnapshot() + sealCer().
    2. Optional node attestation, certifyAndAttestDecision() or attest() for a signed receipt.
    3. Verify, Local: verify(bundle). Offline node stamp: verifyBundleAttestation(bundle, { nodeUrl }) (if signed receipt present).

    Step 1, Create a CER bundle

    import { certifyDecision } from '@nexart/ai-execution'; const cer = certifyDecision({ provider: 'openai', model: 'gpt-4o', prompt: 'Summarize.', input: userQuery, output: llmResponse, parameters: { temperature: 0.7, maxTokens: 1024, topP: null, seed: null }, }); console.log(cer.certificateHash); // "sha256:..."

    Step 2, Node attestation (optional)

    import { certifyAndAttestDecision } from '@nexart/ai-execution'; const { bundle, receipt } = await certifyAndAttestDecision( { provider: 'openai', model: 'gpt-4o', prompt: 'Classify sentiment.', input: customerMessage, output: sentimentResult, parameters: { temperature: 0, maxTokens: 64, topP: null, seed: null }, }, { nodeUrl: 'https://node.nexart.io', apiKey: process.env.NEXART_NODE_API_KEY, } ); console.log(receipt.attestationId); // "att-xyz..." console.log(receipt.signatureB64Url); // Ed25519 signature

    Step 3, Verify

    import { verify, verifyBundleAttestation } from '@nexart/ai-execution'; // Local integrity const result = verify(bundle); console.log(result.ok); // true console.log(result.code); // "OK" // Offline node stamp (if signed receipt present) const stamp = await verifyBundleAttestation(bundle, { nodeUrl: 'https://node.nexart.io', }); console.log(stamp.ok); // true console.log(stamp.code); // "OK"

    Overview

    AI Execution Integrity is an execution surface built on NexArt Protocol v1.2.0. It standardizes how AI and LLM runs are captured, sealed, and audited.

    Every time you call an AI model, the SDK captures what you sent, what you got back, and the exact parameters used. It computes SHA-256 hashes of everything and seals the record into a Certified Execution Record (CER). Any post-hoc modification to protected fields invalidates the certificate hash. AI Execution Integrity defines a standard way to capture and seal AI execution records (CER) so they can be verified independently over time.

    What this certifies

    • Integrity of the recorded execution, cryptographic binding between inputs, parameters, and outputs
    • Tamper evidence, any modification to the record is detectable
    • Chain-of-custody signal, optional node attestation provides independently verifiable proof of integrity at time of attestation

    What this does not certify

    • Determinism, LLMs are not deterministic. Re-running the same prompt may produce different outputs.
    • Provider identity, the record does not verify that the stated provider actually ran the model.
    • Output correctness, integrity attestation does not guarantee truthfulness or quality.

    CER Bundle Format

    A Certified Execution Record wraps a snapshot into a verifiable envelope:

    {
      "bundleType": "cer.ai.execution.v1",
      "certificateHash": "sha256:...",
      "createdAt": "2026-02-12T00:00:00.000Z",
      "version": "0.1",
      "snapshot": { ... },
      "meta": { "source": "my-app", "tags": ["production"] }
    }

    Certificate hash computation

    The certificateHash is SHA-256 of the UTF-8 bytes of the canonical JSON of exactly four fields:

    sha256(canonicalJson({ bundleType, version, createdAt, snapshot }))

    Everything else is excluded from the certificate hash, regardless of where it appears in the bundle:

    • meta, excluded (user metadata, tags, etc.)
    • receipt, signature, attestorKeyId, excluded (attestation fields)
    • attestationId, nodeRuntimeHash, excluded (legacy attestation fields)
    • Any other top-level or nested attestation data, excluded

    Key ordering is recursive (canonical JSON). This computation is identical across all SDK versions.

    Snapshot Format (ai.execution.v1)

    Required core fields

    FieldTypeNotes
    executionIdstringCaller-supplied unique ID
    providerstringe.g. "openai", "anthropic"
    modelstringe.g. "gpt-4o"
    promptstringSystem prompt
    inputstring | objectUser input
    outputstring | objectModel output
    parameters.temperaturenumberMust be finite
    parameters.maxTokensnumberMust be finite

    Optional fields

    FieldTypeDefault
    timestampstringISO 8601; defaults to now()
    modelVersionstring | nullnull
    parameters.topPnumber | nullnull
    parameters.seednumber | nullnull
    sdkVersionstring | nullnull
    appIdstring | nullnull
    runIdstring | nullWorkflow run ID
    stepIdstring | nullStep identifier within a run
    stepIndexnumber | null0-based step position
    workflowIdstring | nullWorkflow template ID
    conversationIdstring | nullConversation/session ID
    prevStepHashstring | nullcertificateHash of previous step

    Redaction and sanitization

    You may need to redact sensitive fields (PII, proprietary prompts) before storing or sharing a CER.

    • Delete the key, safe. If protected fields are modified, the certificateHash will change.
    • Set to null: safe. Modifying protected fields produces a new valid certificateHash for the updated record.
    • Never set to undefined: undefined is not valid JSON and will break canonical serialization.

    Before archiving or attesting, call sanitizeForAttestation(bundle) to strip any undefined values and reject non-serializable types (BigInt, functions, symbols):

    import { sanitizeForAttestation } from '@nexart/ai-execution'; // Deep-clones the bundle, removes undefined keys, rejects BigInt/functions const clean = sanitizeForAttestation(bundle);

    Auto-generated fields

    These are set by createSnapshot(), do not set them manually:

    • type, always "ai.execution.v1"
    • protocolVersion, "1.2.0" (default) or "1.3.0" (opt-in, RFC 8785 JCS)
    • executionSurface, always "ai"
    • inputHash, SHA-256 of input (strings: raw UTF-8 bytes; objects: canonical JSON bytes)
    • outputHash, SHA-256 of output (strings: raw UTF-8 bytes; objects: canonical JSON bytes)

    Canonical Hashing Rules

    certificateHash inputs

    The certificateHash is computed over the canonical JSON serialisation of the following top-level fields, in the exact set the SDK applies today:

    • bundleType
    • version
    • createdAt
    • snapshot
    • context (if present)
    • contextSummary (if present)
    • policyEvaluation (if present)

    Attestation and meta fields (receipt, signature, attestorKeyId, meta.*) are excluded by design so that adding a stamp does not change the certificate hash.

    inputHash / outputHash

    • String values, hashed as raw UTF-8 byte sequences (no canonicalization needed)
    • Object values, serialized to canonical JSON first, then hashed as UTF-8 bytes
    // String input → hash raw UTF-8 bytes inputHash = "sha256:" + sha256(utf8Bytes("What is 2+2?")) // Object input → canonicalize first, then hash inputHash = "sha256:" + sha256(utf8Bytes(canonicalJson({ locale: "en-US", text: "Hello" })))

    Canonicalisation schemes

    From deterministic to standardised verification

    Protocol 1.2.0, deterministic NexArt canonicalisation (nexart-v1). Default and most widely deployed. Frozen, internally specified.

    Protocol 1.3.0, RFC 8785 (JSON Canonicalisation Scheme, JCS). Opt-in. Moves canonicalisation to a public standard, allowing independent verification using off-the-shelf JCS and Ed25519 libraries without NexArt-specific implementation.

    Protocol 1.3.0 is available via the SDK today (protocolVersion: "1.3.0") and can be enabled in production integrations. A verifier MUST canonicalise using the scheme indicated by the record's protocolVersion / version field.

    nexart-v1 (Protocol 1.2.0, default)

    • Object keys sorted lexicographically (Unicode codepoint order) at every nesting level
    • No whitespace between tokens
    • Array order preserved
    • null serialized as null
    • Numbers must be finite, NaN, Infinity, -Infinity rejected (throw)
    • undefined values in object properties are omitted (key dropped), use null instead
    • BigInt, functions, Symbol rejected (throw)
    • String escaping follows JSON.stringify in the host engine. This is pragmatic JCS-equivalent for typical SDK inputs, but is not a verified RFC 8785 implementation: engine-defined behaviour for lone surrogates and certain Unicode escapes may differ from a strict JCS encoder.

    RFC 8785 JCS (Protocol 1.3.0, opt-in)

    • Full conformance with RFC 8785 string escaping, key sorting (UTF-16 code units), and number serialisation (ECMAScript Number.prototype.toString with the IEEE 754 shortest round-trip).
    • Intended for verifiers that require an external published canonicalisation specification.
    • Records are not rewritten from 1.2.0 to 1.3.0; producers select the version explicitly.

    Hash format

    sha256:<64 lowercase hex characters>

    Canonicalisation is frozen per protocol version. Any future stricter canonicalisation ships under a new protocolVersion, never as a silent modification to an existing version.

    Verification Semantics

    The verification model is layered. Each layer answers a different question, and each has a distinct trust boundary.

    LayerWhat it provesTrust required
    Integrity (verify(bundle) / node recompute)Hashes recompute to the recorded values. The record has not been altered.None. Anyone with the bundle and a SHA-256 library can check this.
    Authenticity (Ed25519 signature on the receipt)A specific key signed this exact certificateHash.Trust in the binding between the public key and the node operator.
    Ordering (node-issued timestamp on the receipt)Provides a node-asserted ordering for the receipt.Trust in the node's clock, unless an external TSA is integrated.
    Classification (SDK verifyCer())Additive classification of failure modes, reason codes, and stamp completeness.Convenience layer. The node recomputes verification independently and aligns with SDK classification.

    Status field semantics

    • status: "verified" = integrity match only. It does not assert that a signature was checked.
    • checks.signature must equal "pass" for authenticity.
    • An unsigned bundle may still return status: "verified"; this means integrity passed and no signed receipt was present to verify.
    • verifyCer() provides classification; it is not the sole verification authority. A third party can recompute integrity and verify the signature without calling verifyCer().

    Timestamp and Transparency

    Timestamp

    The attestedAt field on a signed receipt is a node-issued timestamp. It provides an ordering guarantee against other receipts issued by the same node and is serialised in a structure compatible with RFC 3161 timestamp tokens.

    • It is not itself an external RFC 3161 Time-Stamp Authority token; on its own it does not constitute independent third-party proof of existence at a wall-clock time.
    • Independent wall-clock proof is provided by a separate RFC 3161 timestamp issued via DigiCert's public timestamp authority, which is applied to every Certified Execution Record by default and travels with the record without changing its format. A dedicated contracted TSA is available for enterprise customers when required.

    Transparency / anchoring

    The node maintains an internal append-only log of issued receipts. This provides internal consistency for the node operator.

    • The log is node-internal. Inclusion is not externally verifiable today.
    • There is no public Merkle log, no public witness, and no third-party inclusion proof in the current implementation.
    • External anchoring (for example, posting Merkle roots to a public chain) is roadmap-grade and does not change the record format when added.

    Trust Boundaries

    What the substrate guarantees, and where trust still has to come from somewhere else:

    • Integrity, fully independent. Any verifier with the bundle and SHA-256 can recompute it.
    • Authenticity, depends on trust in the binding between the public key and the node operator.
    • Timestamp, two layers: a node-issued timestamp that provides an ordering guarantee within the node's signing chain, and an RFC 3161 timestamp issued via DigiCert's public timestamp authority (applied by default) that provides an independent anchor of when the record existed.
    • Completeness of coverage, not enforced by the substrate. The platform certifies what is submitted to it; ensuring every relevant execution is submitted is an integration-level responsibility.
    • Correctness of the executed program, out of scope. Verification proves integrity of what ran, not that what ran was correct.

    Independent Verification (no NexArt code required)

    A third party can verify a CER using only the bundle JSON, the issuing node's published public key, and standard cryptographic libraries. No NexArt account or runtime is required.

    1. Recompute certificateHash: canonicalise the fields listed in certificateHash inputs using the scheme indicated by the record's protocolVersion (nexart-v1 for 1.2.0; RFC 8785 JCS for 1.3.0), then SHA-256.
    2. Reconstruct the signable payload, canonicalise the receipt object using the same scheme. Cross-check that receipt.certificateHash equals the bundle's certificateHash to prevent receipt-swapping.
    3. Verify the Ed25519 signature, fetch the node's public key set from /.well-known/nexart-node.json, select by kid, and verify with any standard Ed25519 library.

    The canonicalisation scheme must match the protocol version. Verifying a 1.2.0 record with a strict JCS encoder, or a 1.3.0 record with nexart-v1, will produce CERTIFICATE_HASH_MISMATCH.

    Node Attestation vs Signed Receipt Stamp

    A CER bundle can exist in three attestation states:

    StateFields PresentWhat You Can Verify
    Not attestedNoneLocal integrity only (verify(bundle))
    Legacy attestation (stamp incomplete)attestationId, nodeRuntimeHashLocal integrity only, no signature to verify
    Signed receipt (v0.5.0+, recommended)receipt, signature, attestorKeyIdLocal integrity + offline Ed25519 signature verification

    Attestation does not re-run the model; it stamps the integrity of the submitted record.

    Attestation States, Quick Reference

    • Not attested, verify(bundle) proves integrity of the record locally.
    • Stamped (legacy), has attestationId / nodeRuntimeHash; no signature to verify offline.
    • Stamped (signed), has Ed25519 signed receipt; verifiable offline via verifyBundleAttestation().

    Signed receipt verification fetches the node's public keys and checks the Ed25519 signature offline:

    import { verifyBundleAttestation } from '@nexart/ai-execution'; const result = await verifyBundleAttestation(bundle, { nodeUrl: 'https://node.nexart.io', }); // result.ok === true → signature valid // result.code → CerVerifyCode enum value

    Node keys endpoint

    Public keys are published at:

    GET {nodeUrl}/.well-known/nexart-node.json

    View live node keys →

    Keys are provided in JWK, SPKI, and raw Base64url formats. The activeKid field indicates which key is used for new attestations. Historical keys remain for backward-compatible verification. See Node Stamps & Keys for full details.

    Reason Codes

    Every verification call returns a machine-readable code:

    CodeMeaning
    OKAll hashes match. Record is intact.
    CERTIFICATE_HASH_MISMATCHBundle seal doesn't match contents, record was modified.
    INPUT_HASH_MISMATCHInput was changed after sealing.
    OUTPUT_HASH_MISMATCHOutput was changed after sealing.
    SNAPSHOT_HASH_MISMATCHBoth input and output hashes are wrong.
    INVALID_SHA256_FORMATA hash field doesn't start with sha256:.
    SCHEMA_ERRORWrong bundleType/version, missing fields, non-finite parameters.
    CANONICALIZATION_ERRORCanonical JSON serialization threw during verification.
    ATTESTATION_MISSINGNo signed receipt found in bundle.
    ATTESTATION_KEY_NOT_FOUNDkid not found in node keys document.
    ATTESTATION_INVALID_SIGNATUREEd25519 signature did not verify.
    ATTESTATION_KEY_FORMAT_UNSUPPORTEDKey cannot be decoded.
    UNKNOWN_ERRORCatch-all for unclassified failures.

    Priority when multiple failures exist: CANONICALIZATION_ERROR > SCHEMA_ERROR > INVALID_SHA256_FORMAT > CERTIFICATE_HASH_MISMATCH > hash-level codes > UNKNOWN_ERROR. Codes are stable. New codes may be added but existing ones will not be renamed or removed.

    Try It

    From the blog

    This execution surface is additive and does not modify Code Mode Protocol v1.2.0. For step-by-step certification guides, see AI Execution Certification. For the core protocol, see Protocol Overview.