# Project Bundle Registration

URL: https://docs.nexart.io/docs/project-bundle-registration

Registration turns a locally verified bundle into a publicly verifiable artifact on verify.nexart.io.

##### Local verification is not public verification

A Project Bundle that passes `verifyProjectBundle()` locally is not publicly verifiable until it is registered on the node. It will NOT resolve on verify.nexart.io.

## When to use registration

Use when

- Multiple steps or agents
- Workflow-level public verification
- External auditor must resolve without your SDK
- Node-signed receipt required as trust anchor

Skip when

- Single execution (use Path A + `/api/attest`)
- Independent decisions, not a workflow
- Internal-only audit trail, never resolved publicly

## The three verification layers

These are distinct and not interchangeable. A bundle can pass one and still need the next.

- Local SDK verification. `verifyProjectBundle(bundle)` recomputes hashes and checks step integrity against the bundle's own contents. It proves the artifact has not been modified since it was produced.
- Node registration. `POST /v1/project-bundle/register` persists the bundle in the node's proof tables and returns a signed receipt. This is the trust anchor for any third party.
- Public verification. `verify.nexart.io/p/{projectHash}` resolves the registered bundle and re-runs verification against the node's stored copy. Anyone with the projectHash can verify, without your code or your SDK.

## Minimal end-to-end flow

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

// 1. Produce step CERs (each step is its own verifiable record).
const stepA = await certifyDecision({ /* step A inputs */ });
const stepB = await certifyDecision({ /* step B inputs */ });

// 2. Group steps into a Project Bundle (computes projectHash).
const bundle = await createProjectBundle({
  projectId: "workflow-123",
  steps: [stepA.cer, stepB.cer],
});

// 3. Local verification - integrity only.
const local = await verifyProjectBundle(bundle);
if (!local.ok) throw new Error("Bundle is not internally consistent");

// 4. Register on the node - REQUIRED for public verification.
const reg = await registerProjectBundle(bundle, {
  apiKey: process.env.NEXART_API_KEY!,
});

console.log("Public URL:", `https://verify.nexart.io/p/${reg.projectHash}`);
```

## Successful registration response

```
{
  "ok": true,
  "projectHash": "sha256:7d4a...e91",
  "registeredAt": "2026-04-21T12:00:00Z",
  "receipt": {
    "alg": "Ed25519",
    "kid": "node-key-2026-01",
    "sig": "base64..."
  }
}
```

## What the registration helper does

- POSTs the canonical bundle JSON to `/v1/project-bundle/register`.
- Authenticates with your `NEXART_API_KEY`.
- Receives a node-signed receipt over the bundle's `projectHash`.
- Is idempotent: re-registering the same bundle returns the existing record.

## Failure modes

For a complete catalogue of node responses and what to do about them, see Verification Statuses & Errors. The most common ones at registration time:

- `HASH_MISMATCH` - the recomputed projectHash does not match the value in the submitted bundle. Usually caused by non-canonical JSON (extra whitespace, undefined values, key reordering).
- `PERSISTENCE_FAILED` - node accepted the request but could not write to its proof tables. Retry with backoff; the operation is idempotent on `projectHash`.
- `AUTH_INVALID` - missing or rejected API key.

##### Builder checklist before claiming "it works"

- Each step CER verifies locally
- Project Bundle verifies locally
- Bundle registered on the node (success response received)
- `verify.nexart.io/p/{projectHash}` resolves

## Related

- End-to-End Verification Flow
- Project Bundles concept
- Verification Statuses & Errors
- Multi-step & Multi-agent Workflows
