Minimum Integration, 3 Steps
certifyDecision(): Seal input, output, and parameters into a CER bundle. Returns acerobject.certifyAndAttestDecision(): Same as above, plus submit to a node for a signed receipt. Returns{ bundle, receipt }.- Verify,
verify(cer)after Step 1;verify(bundle)after Step 2. AddverifyBundleAttestation(bundle, { nodeUrl })if a signed receipt is present.
Step 1, certifyDecision()
The simplest path. Pass your LLM call details and get a sealed CER bundle object in one call.
import { certifyDecision } from '@nexart/ai-execution';
const cer = certifyDecision({
provider: 'openai',
model: 'gpt-4o',
prompt: 'Summarize the document.',
input: userQuery,
output: llmResponse,
parameters: {
temperature: 0.7,
maxTokens: 1024,
topP: null,
seed: null,
},
});
console.log(cer.certificateHash);
// "sha256:a1b2c3..."
console.log(cer.bundleType);
// "cer.ai.execution.v1"The returned cer object is a complete CER bundle. Its certificateHash is a SHA-256 digest of the canonical JSON of { bundleType, version, createdAt, snapshot }. Any post-hoc change to the input, output, or parameters invalidates it.
Step 2, certifyAndAttestDecision()
One-call integration: certifies the decision and submits it to a canonical node for a signed receipt. Returns { bundle, receipt }, the sealed CER bundle and the node's signed attestation receipt.
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://nexart-canonical-renderer-production.up.railway.app',
apiKey: process.env.NEXART_NODE_API_KEY,
}
);
console.log(bundle.certificateHash);
// "sha256:d4e5f6..."
console.log(receipt.attestationId);
// "att-xyz789..."
console.log(receipt.signatureB64Url);
// Ed25519 signature over the receipt
console.log(receipt.attestorKeyId);
// Key ID for offline verificationSkip re-attestation: Use attestIfNeeded(bundle, options) to avoid double-attestation. It checks for an existing receipt before making a network call.
Step 3, Verify
Verification works in two layers. Use the object returned by the step you chose:
import { verify, verifyBundleAttestation } from '@nexart/ai-execution';
// After Step 1 (certifyDecision), pass the cer object directly:
const result = verify(cer);
console.log(result.ok); // true
console.log(result.code); // "OK"
// After Step 2 (certifyAndAttestDecision), pass the bundle:
const result2 = verify(bundle);
console.log(result2.ok); // true
// Node stamp verification, verifies Ed25519 signed receipt (if present)
const stamp = await verifyBundleAttestation(bundle, {
nodeUrl: 'https://nexart-canonical-renderer-production.up.railway.app',
});
console.log(stamp.ok); // true
console.log(stamp.code); // "OK"What This Certifies
Integrity, not determinism. A CER certifies that the recorded input, output, and parameters have not been modified after the fact. It does not guarantee that the AI model will produce the same output again, LLMs are not deterministic. It also does not verify provider identity.
- PASS, The record is internally consistent. Hashes match the sealed payload.
- FAIL, Integrity breach. The record does not match its hashes.
- ERROR, Missing fields or invalid formatting.
Redaction Guidance
You may need to redact sensitive fields (PII, proprietary prompts) before sharing a CER bundle.
Redaction invalidates verification, by design. Once a CER bundle is sealed, any modification to hashed fields will cause verify() to return CERTIFICATE_HASH_MISMATCH. This is the intended behavior: it proves the original record was tamper-free at sealing time.
Recommended patterns:
- Redact before sealing, Remove sensitive fields before calling
certifyDecision(). The sealed bundle will verify cleanly with the redacted content. - Store full + share redacted, Seal the full bundle for your private archive, then share a redacted copy externally. The redacted copy won't pass
verify(), but the original will.
| Action | Effect on Verification | Recommended |
|---|---|---|
| Delete the key | Hash mismatch (expected) | ✅ Yes |
Set to null | Hash mismatch (expected) | ✅ Yes |
Set to undefined | Breaks canonical JSON serialization | ❌ Never |
Use sanitizeForAttestation(bundle) before archiving, it strips undefined values and rejects non-serializable types that would break canonical JSON.
Store the Stamp
If you used certifyAndAttestDecision(), persist the receipt fields alongside the bundle so offline stamp verification works later, without needing to contact the node again.
// Persist these fields from the receipt:
await db.insert('cer_bundles', {
certificate_hash: bundle.certificateHash,
cer_bundle: bundle,
// Required for offline stamp verification
attestation_id: receipt.attestationId,
signature_b64url: receipt.signatureB64Url,
attestor_key_id: receipt.attestorKeyId, // kid
});The attestorKeyId (kid) maps to the node's public key published at its .well-known/nexart-node.json endpoint. This enables fully offline Ed25519 signature verification.
Multi-Step Workflows
For agentic pipelines with multiple LLM calls, use RunBuilder to chain steps with prevStepHash linking:
import { RunBuilder } from '@nexart/ai-execution';
const run = new RunBuilder({
runId: 'analysis-run',
workflowId: 'data-pipeline',
});
run.step({
provider: 'openai',
model: 'gpt-4o',
prompt: 'Plan the analysis.',
input: 'Analyze Q1 sales data.',
output: 'I will: 1) load data, 2) compute totals, 3) summarize.',
parameters: { temperature: 0.3, maxTokens: 512, topP: null, seed: null },
});
run.step({
provider: 'openai',
model: 'gpt-4o',
prompt: 'Execute step 1.',
input: 'Load and total Q1 data.',
output: 'Total revenue: $1.2M.',
parameters: { temperature: 0.3, maxTokens: 512, topP: null, seed: null },
});
const summary = run.finalize();
// { runId, stepCount: 2, steps: [...], finalStepHash: "sha256:..." }Try It
Issue → Verify → Audit:
- Issue an AI CER at nexartaiauditor.xyz
- Download the CER JSON
- Verify independently at verify.nexart.io: upload the JSON to audit integrity