AI Execution Certification

    I call an LLM, how do I certify that record?

    Minimum Integration, 3 Steps

    1. certifyDecision(): Seal input, output, and parameters into a CER bundle. Returns a cer object.
    2. certifyAndAttestDecision(): Same as above, plus submit to a node for a signed receipt. Returns { bundle, receipt }.
    3. Verify, verify(cer) after Step 1; verify(bundle) after Step 2. Add verifyBundleAttestation(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 verification

    Skip 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.
    ActionEffect on VerificationRecommended
    Delete the keyHash mismatch (expected)✅ Yes
    Set to nullHash mismatch (expected)✅ Yes
    Set to undefinedBreaks 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:

    1. Issue an AI CER at nexartaiauditor.xyz
    2. Download the CER JSON
    3. Verify independently at verify.nexart.io: upload the JSON to audit integrity