# From Execution to Public Verification

URL: https://docs.nexart.io/docs/end-to-end-verification

The complete lifecycle from local integrity to node attestation to public verification. Three trust states, not interchangeable.

##### A bundle that verifies locally is not publicly verifiable

A Project Bundle that passes `verifyProjectBundle()` locally but has NOT been registered on the attestation node will NOT resolve on`verify.nexart.io/p/{projectHash}`. Registration is a separate, REQUIRED step.

## The Full Lifecycle

Every verifiable execution moves through the same seven stages. Skipping any stage between local verification and public verification breaks the trust chain.

Lifecycle

```
1. Execute AI step                     (your application)
2. Create CER                          certifyDecision(...)
3. (Optional) Attest CER on node       POST /api/attest
4. Build Project Bundle                createProjectBundle({ steps: [...] })
5. Verify locally                      verifyProjectBundle(bundle)
6. Register bundle on node  REQUIRED   POST /v1/project-bundle/register
7. Public verification                 verify.nexart.io/p/{projectHash}
```

Trust chain

```
Execution -> CER -> Bundle -> Local Verify -> Node Register -> Public Verify
                                  (integrity)   (trust anchor)   (independent witness)
```

## Three Levels of Verification

NexArt provides three distinct verification levels. They answer different questions and are NOT interchangeable.

### Level 1 - Local SDK Verification (integrity)

Proves the artifact has not been modified since it was produced.

- `verifyCer(bundle)`
- `verifyProjectBundle(bundle)`
- `verifyCerAsync` / `verifyProjectBundleAsync` (browser)

What it proves: Bundle integrity. The recomputed`certificateHash` /`projectHash` matches the bundle.

What it does NOT prove: That any third party has witnessed the artifact. Local-only artifacts cannot be looked up on`verify.nexart.io`.

### Level 2 - Node Certification / Registration (trust anchor)

Submits the artifact to the attestation node, which signs a receipt and writes the artifact to its proof tables.

- `POST /api/attest` - attest a single CER
- `POST /v1/project-bundle/register` - register a Project Bundle

What it produces: An Ed25519-signed receipt anchored to the node's public key, plus a durable proof record on the node. This is what anchors trust.

### Level 3 - Public Verification (independent witness)

Anyone can verify a node-registered artifact without your API key.

- `https://verify.nexart.io/c/{certificateHash}` - single CER
- `https://verify.nexart.io/p/{projectHash}` - Project Bundle

Requires: A node-registered artifact. If the artifact was only verified locally, the public verifier returns`NOT_FOUND`.

## Project Bundle Registration (REQUIRED for public verify)

This is the step most often missed. Local verification is not a substitute for registration.

Endpoint

```
POST https://node.nexart.io/v1/project-bundle/register
Authorization: Bearer <NEXART_API_KEY>
Content-Type: application/json

<full cer.project.bundle.v1 JSON returned by createProjectBundle()>
```

### Response

A successful response includes:

- `attestationId` - node-assigned identifier for the registered bundle
- `verificationEnvelope` - canonical envelope covering the project-level receipt
- `signature` - Ed25519 signature over the envelope by the node's signing key
- `metadata` - node identity, key id, timestamp, protocol version

### Success Definition

Registration is successful ONLY when ALL of the following are true:

- The node returns a 2xx response with the fields above.
- The bundle is written to the node's proof tables.
- `https://verify.nexart.io/p/{projectHash}` resolves and returns `VERIFIED`.

A 200 response alone is not sufficient evidence of success. Always confirm against the public verifier.

## Minimal Correct Flow

End-to-end (Node / TypeScript)

```
import {
  certifyDecision,
  createProjectBundle,
  verifyProjectBundle,
} from "@nexart/ai-execution";

// 1. Generate CERs for each step
const cer1 = await certifyDecision({ /* step 1 */ });
const cer2 = await certifyDecision({ /* step 2 */ });
const cer3 = await certifyDecision({ /* step 3 */ });

// 2. Build the Project Bundle
const bundle = createProjectBundle({
  projectTitle: "Contract review pipeline",
  steps: [cer1, cer2, cer3],
});

// 3. Local integrity check (does NOT make it publicly verifiable)
const local = verifyProjectBundle(bundle);
if (local.status !== "VERIFIED") {
  throw new Error("Local verification failed");
}

// 4. REQUIRED: register the bundle on the attestation node.
// Without this step, the bundle will NOT resolve on verify.nexart.io.
const res = await fetch("https://node.nexart.io/v1/project-bundle/register", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.NEXART_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(bundle),
});

if (!res.ok) {
  throw new Error(`Bundle registration failed: ${res.status}`);
}

const { attestationId } = await res.json();
const projectHash = bundle.integrity.projectHash;

// 5. Public verification URL (share this, not the bundle JSON)
console.log(`https://verify.nexart.io/p/${encodeURIComponent(projectHash)}`);
```

## Common Failure Cases

### Bundle verifies locally but not on verify.nexart.io

Cause: The bundle was never registered on the node.`verifyProjectBundle()`only checks integrity locally; it does not contact the node.

Fix: Call `POST /v1/project-bundle/register` with the full bundle JSON and a valid API key.

### Node returns 200 but bundle not found later

Possible causes:

- Wrong endpoint (e.g. calling `/api/attest` with a project bundle instead of `/v1/project-bundle/register`).
- Wrong payload shape (sending a single CER instead of the full `cer.project.bundle.v1` object).
- Wrong environment (staging API key against production node, or vice versa).
- Missing or malformed `Authorization` header.
- Bundle was written to a non-persistent code path (e.g. a dry-run flag, a test mode, or a proxy that swallows the body).

Fix: Confirm the response includes`attestationId` and`signature`, then resolve`verify.nexart.io/p/{projectHash}` as the authoritative success signal.

### "Partially Verified" confusion

The verifier reports per-pillar results (Bundle Integrity, Node Signature, Receipt Consistency, Verification Envelope). A partial result usually means one of:

- The bundle has no node attestation, so node-dependent checks return `SKIPPED`.
- Context fields outside the certificateHash scope have been edited; they do not break integrity but are not covered by the hash.
- Different verifier versions evaluate the same artifact at different protocol versions; minor versions are forward-compatible, breaking changes require a new namespace.

See Verification Reports for the full per-pillar semantics.

## Builder Checklist

Before claiming an integration "works", confirm every item:

- CER verifies locally with `verifyCer()`.
- Project Bundle verifies locally with `verifyProjectBundle()`.
- Bundle registered on the node - response includes `attestationId` and `signature`.
- Bundle appears in node proof tables (lookup by `projectHash` succeeds).
- `verify.nexart.io/p/{projectHash}` resolves and returns `VERIFIED`.

## Trust Model in One Sentence

SDK proves integrity. Node anchors trust. Verifier provides an independent check. See Trust Model for the full breakdown of who signs what and why each role is necessary.

## See also

- Project Bundle Registration - the registration step in detail.
- Verification Statuses & Errors - what every result and error means.
- Multi-step & Multi-agent Workflows - applying this to agents.
- Public Reseals & Redacted Verification - why public hashes can differ.
