nexart.iodocs

    Certifying LLM Conversations

    End-to-end pattern for producing per-message and per-conversation Certified Execution Records inside your own conversational AI application.

    This guide shows how to integrate NexArt into a conversational AI application so that every user turn produces a Certified Execution Record (CER), and the full conversation is sealed into a Project Bundle that any third party can verify. It assumes you already have a working LLM loop and are familiar with the AI Execution CER concept.

    The pattern below is stable across minor SDK updates of @nexart/ai-execution. Examples are written against >= 0.14.0 and verified against the current 0.16.1 release.

    On this page

    1. Why two layers: per-message and per-conversation
    2. What certification proves and what it doesn't
    3. Certifying a single turn
    4. Sealing the conversation
    5. After-the-fact verification
    6. Redaction before sealing

    Why two layers: per-message and per-conversation

    A conversation is not a single execution. It is a sequence of executions, each of which is independently meaningful and independently auditable. NexArt mirrors that structure with two layers of certification:

    • Per-message CER. One Certified Execution Record per LLM turn. Each turn's prompt, model parameters, and response are hashed and signed in isolation. A turn CER is verifiable on its own, even if the rest of the conversation is unavailable.
    • Per-conversation Project Bundle. When the conversation ends (or reaches a natural checkpoint), the turn CERs are grouped into a Project Bundle. The bundle commits to the ordered set of turns under a single projectHash, and registering it produces a publicly verifiable artifact at verify.nexart.io/p/{projectHash}.

    Both layers are required for end-to-end certification. A turn CER without a bundle cannot prove the conversation it belonged to. A bundle without registered turn CERs cannot prove the content of any individual turn.

    What certification proves and what it doesn't

    NexArt provides cryptographic proof of execution integrity. It does not validate semantic correctness. Be explicit with stakeholders about what a green verification result actually means.

    What this proves

    • The exact prompt, model identity, parameters, and response existed at the recorded time.
    • Nothing in the certified payload has been modified since sealing.
    • The NexArt attestation node witnessed and signed the record.
    • The ordered sequence of turns in the conversation is fixed.

    What this does NOT prove

    • That the model's response is correct, safe, or non-hallucinated.
    • That the end user's identity is verified (unless you attach identity signals).
    • That any field redacted after sealing is recoverable.
    • That the model used was the "best" or "right" model for the task.

    Certifying a single turn

    Wrap each LLM call so that the prompt, the model parameters, and the response are committed to a CER. Then post the CER to the attestation node, which returns a signed receipt.

    import { certifyDecision } from "@nexart/ai-execution";
    
    async function certifyTurn(args: {
      conversationId: string;
      turnIndex: number;
      userMessage: string;
      systemPrompt: string;
      model: string;
      temperature: number;
      assistantResponse: string;
    }) {
      const { cer } = await certifyDecision({
        executionId: `${args.conversationId}:turn-${args.turnIndex}`,
        input: {
          systemPrompt: args.systemPrompt,
          userMessage: args.userMessage,
        },
        output: {
          assistantResponse: args.assistantResponse,
        },
        model: {
          id: args.model,
          parameters: { temperature: args.temperature },
        },
        executionContext: {
          conversationId: args.conversationId,
          turnIndex: args.turnIndex,
        },
      });
    
      // Attest the single turn on the node.
      const res = await fetch("https://node.nexart.io/api/attest", {
        method: "POST",
        headers: {
          "Authorization": `Bearer ${process.env.NEXART_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify(cer),
      });
    
      if (!res.ok) {
        throw new Error(`Attestation failed: ${res.status}`);
      }
    
      const { receipt } = await res.json();
      return { cer, receipt };
    }

    Persist cer alongside the turn in your application database. You will need every turn CER when you seal the conversation in the next step.

    Sealing the conversation

    When the conversation ends, group the ordered turn CERs into a Project Bundle and register it. The bundle commits to the ordering of turns under a single projectHash.

    import {
      createProjectBundle,
      verifyProjectBundle,
      registerProjectBundle,
    } from "@nexart/ai-execution";
    
    async function sealConversation(conversationId: string, turnCers: unknown[]) {
      // 1. Group turns. Order matters - stepIndex implies turn order.
      const bundle = await createProjectBundle({
        projectId: conversationId,
        steps: turnCers.map((cer, stepIndex) => ({
          cer,
          stepIndex,
        })),
      });
    
      // 2. Verify integrity locally before sending anything to the node.
      const local = await verifyProjectBundle(bundle);
      if (!local.ok) {
        throw new Error("Bundle failed local verification - do not register");
      }
    
      // 3. Register on the node. REQUIRED for public verification.
      const reg = await registerProjectBundle(bundle, {
        apiKey: process.env.NEXART_API_KEY!,
      });
    
      return {
        projectHash: reg.projectHash,
        publicUrl: `https://verify.nexart.io/p/${reg.projectHash}`,
      };
    }

    This example treats the conversation as linear: turn N follows turn N-1. Branching conversations and multi-agent handoffs use additional step-graph fields; see Multi-step and Multi-agent Workflows for that pattern.

    After-the-fact verification

    Once a conversation is sealed and registered, anyone with the projectHash can verify it independently of your application, your SDK, and your code. There are three layers and they are not interchangeable.

    1. Local integrity. verifyProjectBundle(bundle) recomputes hashes against the bundle's own contents. Useful inside your application before registration or when reprocessing exports.
    2. Per-turn public verification. verify.nexart.io/c/{certificateHash} resolves any individual turn CER that was attested via /api/attest.
    3. Per-conversation public verification. verify.nexart.io/p/{projectHash} resolves the registered bundle, validates each step CER, and confirms ordering. This is the artifact you give to auditors.

    See Verification Statuses and Errors for the full catalogue of response shapes and what they mean.

    Redaction before sealing

    Some conversations contain content that should not be exposed to a public verifier: personally identifying information, customer secrets, internal system prompts. NexArt supports redaction, but only at well-defined points in the lifecycle.

    The supported pattern is:

    1. Apply redaction to the prompt and response BEFORE calling certifyDecision.
    2. Let the SDK compute the certificateHash over the redacted payload.
    3. Attest the redacted CER on the node.
    4. Seal and register the bundle.

    If you must retain the unredacted content internally for your own operational use, store it separately from the certified payload and never mix the two streams. The CER is the source of truth for what was witnessed; your internal store is not certified.

    Working with Python? See the Python Bridge page for using NexArt from Python via the canonical JS SDK.