Minimum Integration, 3 Steps
- Create a CER bundle,
certifyDecision()orcreateSnapshot()+sealCer(). - Optional node attestation,
certifyAndAttestDecision()orattest()for a signed receipt. - 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 signatureStep 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
| Field | Type | Notes |
|---|---|---|
executionId | string | Caller-supplied unique ID |
provider | string | e.g. "openai", "anthropic" |
model | string | e.g. "gpt-4o" |
prompt | string | System prompt |
input | string | object | User input |
output | string | object | Model output |
parameters.temperature | number | Must be finite |
parameters.maxTokens | number | Must be finite |
Optional fields
| Field | Type | Default |
|---|---|---|
timestamp | string | ISO 8601; defaults to now() |
modelVersion | string | null | null |
parameters.topP | number | null | null |
parameters.seed | number | null | null |
sdkVersion | string | null | null |
appId | string | null | null |
runId | string | null | Workflow run ID |
stepId | string | null | Step identifier within a run |
stepIndex | number | null | 0-based step position |
workflowId | string | null | Workflow template ID |
conversationId | string | null | Conversation/session ID |
prevStepHash | string | null | certificateHash 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:undefinedis 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:
bundleTypeversioncreatedAtsnapshotcontext(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
nullserialized asnull- Numbers must be finite,
NaN,Infinity,-Infinityrejected (throw) undefinedvalues in object properties are omitted (key dropped), usenullinstead- BigInt, functions, Symbol rejected (throw)
- String escaping follows
JSON.stringifyin 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.toStringwith 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.
| Layer | What it proves | Trust 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.signaturemust 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 callingverifyCer().
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.
- Recompute
certificateHash: canonicalise the fields listed in certificateHash inputs using the scheme indicated by the record'sprotocolVersion(nexart-v1for 1.2.0; RFC 8785 JCS for 1.3.0), then SHA-256. - Reconstruct the signable payload, canonicalise the
receiptobject using the same scheme. Cross-check thatreceipt.certificateHashequals the bundle'scertificateHashto prevent receipt-swapping. - Verify the Ed25519 signature, fetch the node's public key set from
/.well-known/nexart-node.json, select bykid, 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:
| State | Fields Present | What You Can Verify |
|---|---|---|
| Not attested | None | Local integrity only (verify(bundle)) |
| Legacy attestation (stamp incomplete) | attestationId, nodeRuntimeHash | Local integrity only, no signature to verify |
| Signed receipt (v0.5.0+, recommended) | receipt, signature, attestorKeyId | Local 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 valueNode keys endpoint
Public keys are published at:
GET {nodeUrl}/.well-known/nexart-node.jsonKeys 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:
| Code | Meaning |
|---|---|
OK | All hashes match. Record is intact. |
CERTIFICATE_HASH_MISMATCH | Bundle seal doesn't match contents, record was modified. |
INPUT_HASH_MISMATCH | Input was changed after sealing. |
OUTPUT_HASH_MISMATCH | Output was changed after sealing. |
SNAPSHOT_HASH_MISMATCH | Both input and output hashes are wrong. |
INVALID_SHA256_FORMAT | A hash field doesn't start with sha256:. |
SCHEMA_ERROR | Wrong bundleType/version, missing fields, non-finite parameters. |
CANONICALIZATION_ERROR | Canonical JSON serialization threw during verification. |
ATTESTATION_MISSING | No signed receipt found in bundle. |
ATTESTATION_KEY_NOT_FOUND | kid not found in node keys document. |
ATTESTATION_INVALID_SIGNATURE | Ed25519 signature did not verify. |
ATTESTATION_KEY_FORMAT_UNSUPPORTED | Key cannot be decoded. |
UNKNOWN_ERROR | Catch-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
- Issue an AI CER: nexartaiauditor.xyz
- Verify independently: verify.nexart.io: upload a CER JSON to audit integrity in your browser (no data sent to any server)
- Interactive demo: AI Execution Integrity Demo: generate a snapshot, seal a CER, and test tamper detection
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.