# Independent Verification

URL: https://docs.nexart.io/docs/independent-verification

Reproduce the full verification of a NexArt CER using only the bundle, node public keys, SHA-256, Ed25519, and a protocol-matched canonicalizer.

## 1. Overview

A NexArt Certified Execution Record (CER) is a self-contained cryptographic artifact. Any party in possession of the bundle and the issuing node's public key set can verify it independently. The NexArt infrastructure is not in the trust path.

The verifier requires only:

- the CER bundle (JSON);
- the issuing node's public key set;
- SHA-256;
- an Ed25519 signature verifier;
- a JSON canonicalizer matching the bundle's `protocolVersion` (`nexart-v1` for 1.2.0; `jcs-v1` / RFC 8785 for 1.3.0).

No NexArt runtime or SDK is required for verification. The record and public keys can be obtained once and verified offline.

## 2. Verification Steps

### Step 1 — Fetch the record

Fetch the public CER bundle

```
curl -fsS "https://node.nexart.io/v1/cer/public?certificate_hash=<hash>" > record.json
```

### Step 2 — Fetch the node public keys

Fetch the node key set

```
curl -fsS "https://node.nexart.io/.well-known/nexart-node.json" > keys.json
```

See Key Management for the key set schema, `kid` resolution, and rotation policy.

### Step 3 — Verify locally

Verify using the NexArt CLI

```
npx @nexart/cli verify-bundle record.json --public-key keys.json
```

The CLI is a thin wrapper around the checks defined below. Any reimplementation in another language is equivalent, provided it follows the same rules. See External Verification for a reference pseudo-code implementation.

The verifier must also:

- Resolve `receipt.kid` against the node key set;
- Enforce key lifecycle (validity window, revocation status);
- Fail closed on unknown or invalid keys.

## 3. What the Verifier Does

- Selects the canonicalization profile from `snapshot.protocolVersion` (the source of truth). The verifier MUST also assert that `meta.attestation.protocolVersion` equals `snapshot.protocolVersion`; any mismatch fails closed. Anchoring on the attestation alone would mean trusting the signer instead of verifying the record.

```
"1.2.0" -> profile "nexart-v1" (DEFAULT, custom canonicalization)
"1.3.0" -> profile "jcs-v1"    (opt-in, RFC 8785 / JCS)
other   -> FAIL
```

Canonicalization is protocol-bound. RFC 8785 is not universal. A 1.2.0 bundle canonicalized under `jcs-v1` (or vice versa) will not recompute to the same hash.

- Recomputes the SHA-256 over the whitelist projection and compares with `bundle.certificateHash`. The canonicalized byte sequence must be identical: any variation in field order, encoding, or whitespace produces a different hash.

Whitelist projection (fields covered by certificateHash)

```
bundleType
version
createdAt
snapshot
context           (only when present)
contextSummary    (only when present)
policyEvaluation  (only when present)
```

- Verifies the Ed25519 signature over the canonicalized `meta.attestation.receipt.payload` using `meta.attestation.receiptSignature` and the public key whose `kid` matches `receipt.kid`, and confirms `receipt.payload.certificateHash == bundle.certificateHash`.
- Validates signer key lifecycle. Resolves `receipt.kid` against the published key set, enforces the `validFrom` / `validTo` window, rejects revoked keys, and rejects unknown key identifiers.
- (Optional) Verifies the Ed25519 signature on the verification envelope when present. The envelope signature is independent from the receipt signature and covers a different field set. If the envelope is absent, the layer is SKIPPED, not failed.

## 4. Data Model

- `snapshot` — the execution data. On public/redacted bundles, `snapshot.input` and `snapshot.output` are SHA-256 digests rather than raw payloads.
- `certificateHash` — the integrity anchor; a SHA-256 over the canonicalized whitelist projection, formatted as `sha256:<hex>`.
- `meta.attestation.receipt` — the node attestation: an Ed25519-signed payload that binds `certificateHash`, `attestedAt`, `kid`, and `protocolVersion`.
- `meta.verificationEnvelope` — an optional signed metadata layer combining attestation fields with the bundle whitelist projection, signed independently from the receipt.

## 5. Signer Model and Key Lifecycle

A NexArt CER is signed by an attestation node using an Ed25519 key. The node publishes its public key set at `https://node.nexart.io/.well-known/nexart-node.json`.

Each key includes:

- `kid` — key identifier;
- `algorithm` — signing algorithm (Ed25519);
- `status` — `active` | `deprecated` | `revoked`;
- `validFrom` — start of validity window;
- `validTo` — end of validity window (if present);
- `publicKey` — raw base64url key;
- `publicKeyJwk` — JWK representation;
- `publicKeySpkiB64` — SPKI representation.

Verification MUST enforce key lifecycle:

- The `receipt.kid` must exist in the published key set;
- The key must not be revoked;
- The verification timestamp must be within `[validFrom, validTo]` (if `validTo` is present);
- Unknown or invalid keys MUST fail verification.

No fallback key resolution is allowed.

The signer is independent of the execution system. The node acts as an attestation authority and signs the receipt over the `certificateHash`.

## 6. Fail-Closed Behaviour

Every check fails closed. There is no fallback, no coercion, and no silent downgrade.

```
Unknown / missing snapshot.protocolVersion                     -> FAIL
meta.attestation.protocolVersion != snapshot.protocolVersion   -> FAIL
Canonicalization profile mismatch                              -> FAIL (hash will not match)
Recomputed certificateHash != bundle value                     -> FAIL
receipt.kid not in published key set                           -> FAIL
Unknown key identifier (receipt.kid not found)                 -> FAIL
Key revoked                                                    -> FAIL
Key outside validity window                                    -> FAIL
Invalid Ed25519 signature on receipt                           -> FAIL
receipt.payload.certificateHash != bundle hash                 -> FAIL
Invalid Ed25519 signature on envelope (if present)             -> FAIL
```

See Verification Statuses and Errors for the full status table.

## 7. Trust Boundaries

Independent verification of a NexArt CER proves:

- Integrity — the bundle was not altered after sealing.
- Authenticity — a node holding the private key corresponding to `receipt.kid` signed the record.
- Signer validity — the signing key was valid and not revoked at verification time.

It does not prove:

- Independent timestamp — `attestedAt` is a node-issued timestamp providing ordering, not independent proof of existence.
- Completeness — only the recorded execution is attested; the surrounding workflow and external state are not.
- Transparency log inclusion — no public append-only log is currently enforced.
- Semantic correctness — verification proves what executed, not whether it was correct.

## 8. Reproducibility

Every operation specified on this page is deterministic and uses only standard cryptographic primitives. A developer can implement an independent verifier in any language from this page alone; see External Verification for a worked reference and Verification Model for the normative rules.

## 9. Trusted Timestamps (RFC 3161)

As of node v0.18.1, NexArt supports external RFC 3161 timestamps in addition to the node-issued timestamp. External timestamps are obtained from a third-party Time Stamp Authority (TSA). The current default TSA is FreeTSA.

The timestamp is cryptographically bound to the `certificateHash` via the RFC 3161 message imprint. The TSA token is:

- stored and returned alongside the bundle in the certify response;
- NOT included in the `certificateHash`;
- NOT part of the signed receipt payload;
- NOT part of canonicalization (neither `nexart-v1` nor `jcs-v1` / RFC 8785).

This preserves determinism, reproducibility, and backward compatibility. External timestamps are an additive verification layer, not part of the signed record. A verifier that does not implement RFC 3161 still produces the same integrity and authenticity result.

### 9.1 Verification layers

Independent verification is composed of three layers:

- Integrity — recompute SHA-256 over the canonicalized whitelist projection (sections 3 and 4).
- Attestation — verify the Ed25519 signature on the receipt and validate the signer key lifecycle (section 5).
- Time anchoring (optional) — verify the RFC 3161 TSA token against trusted TSA roots and confirm the message imprint equals the bundle `certificateHash`.

Base verification (Layers 1 and 2) does not depend on the TSA. Layer 3 is optional and additive. Failure to verify the TSA token does not invalidate the CER; it only means the external time anchor could not be confirmed.

### 9.2 Timestamp types

- Node-issued (`attestedAt`) — deterministic, always available, proves ordering only. Not independent proof of existence.
- RFC 3161 (external) — non-deterministic, proves existence in time via a third party, requires the verifier to maintain a set of trusted TSA roots.

## 10. What NexArt Proves vs Does Not Prove

NexArt proves:

- what executed — the canonical snapshot;
- that it was not modified — integrity via SHA-256;
- when it existed — only if an RFC 3161 TSA token is present and verifies against trusted roots.

NexArt does NOT prove:

- correctness of output — verification is not validation;
- legal validity — no jurisdictional claims are made;
- identity authenticity — identity fields are claims captured at execution time, not independently verifiable (see Identity Binding).

## 11. Versioning

- `protocolVersion: "1.3.0"` is the default for all new CERs. RFC 8785 JCS (`jcs-v1`) is required for canonicalization.
- `protocolVersion: "1.2.0"` remains supported for backward compatibility and uses the `nexart-v1` custom canonicalization profile.
- Any other value MUST fail closed.

## 12. CLI Verification

The NexArt CLI verifier supports:

- bundle integrity (Layer 1);
- signature verification, including signer key lifecycle (Layer 2);
- (soon) RFC 3161 TSA token verification (Layer 3).

Until TSA verification ships in the CLI, Layer 3 can be verified out-of-band using any RFC 3161-compatible tool (for example `openssl ts -verify`) against the stored TSA token and the bundle `certificateHash` as the message imprint.
