@nexart/cli 0.8.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,298 @@
1
+ # @nexart/cli — Changelog
2
+
3
+ ## v0.10.0 (2026-06-11) — protocolVersion awareness (versioned canonicalisation)
4
+
5
+ Additive and fully backward-compatible. No command, flag, or output format was
6
+ removed or changed.
7
+
8
+ - **`ai verify` now surfaces the bundle's `protocolVersion`** in both human-readable
9
+ and `--json` output (`cli.protocolVersion`), so operators can see which
10
+ canonicalisation mode (`1.2.0`→`nexart-v1`, `1.3.0`→`jcs-v1`) was verified.
11
+ - **Fail-closed behaviour for unknown versions is inherited from the SDK.** All AI
12
+ verification still delegates entirely to `@nexart/ai-execution`
13
+ (`verifyAiCerBundleDetailed`); a record with an unsupported `protocolVersion` is
14
+ reported as `FAILED` (verify code `SCHEMA_ERROR`, reason code
15
+ `SCHEMA_VERSION_UNSUPPORTED`, exit 1) rather than mis-verified. The CLI
16
+ adds no fallback or default.
17
+ - **Requires `@nexart/ai-execution` `^0.17.0`** — the first public release that ships
18
+ `protocolVersion` `1.3.0` (`jcs-v1` / RFC 8785). Earlier public ai-execution
19
+ releases are `1.2.0`-only; pinning to `^0.17.0` is what lets a fresh
20
+ `npm install -g @nexart/cli` verify `1.3.0` records (and fail closed on unknown
21
+ versions).
22
+
23
+ ### Also in v0.10.0 — Enterprise trust layers surfaced in the CLI (additive, NON-evidentiary)
24
+
25
+ All additive; no existing command, flag, or output format was removed or changed. These layers are
26
+ **secondary proofs** — they are reported separately and **never** change the verification result or
27
+ exit code (`VERIFIED` → exit 0, `FAILED` → exit 1 still derive solely from base SDK verification).
28
+
29
+ - **`nexart ai verify --anchors`** — verify and report anchor status (`VALID` / `INVALID` /
30
+ `NOT PRESENT`).
31
+ - **`nexart ai verify --timestamps`** — verify and report trusted-timestamp status; adds a human
32
+ `Layers:` block and a `Trust summary` block (Signature / Anchors / Timestamp / Protocol Version),
33
+ and top-level `anchorStatus` / `timestampStatus` to `--json` output.
34
+ - **`nexart ai attach-anchor <cer> <anchor>`** — append-only attach of an external anchor (no
35
+ re-hash/re-sign).
36
+ - **`nexart export-verification <cer>`** (new top-level command) — emit a self-contained, offline
37
+ verification package (`{ bundle, publicKey, verificationInstructions }`); `--out`, `--node-keys`,
38
+ `--public-key`.
39
+
40
+ ## v0.9.0 (2026-06-08)
41
+
42
+ ### New Features — Deterministic Replay (controlled CodeMode / canonical renders)
43
+
44
+ Additive and fully backward-compatible. No public command, flag, snapshot, or
45
+ verifier-output format was removed or changed; legacy snapshots continue to
46
+ verify and replay exactly as before.
47
+
48
+ - **`--renderer local` is now a real deterministic renderer.** `run`, `replay`,
49
+ and `verify` render locally through the canonical CodeMode render core
50
+ (`renderStaticPng` from `@nexart/codemode-sdk/node`) and hash the encoded PNG
51
+ bytes — the same `outputHash` definition as the remote renderer. The previous
52
+ 1×1 placeholder behavior is gone; a missing optional `canvas` dependency now
53
+ fails clearly (exit 1, install hint) instead of producing a false pass.
54
+ - **`nexart replay` performs real load → render → compare.** It reports
55
+ `PASS`/`FAIL` (exit `0`/`1`), comparing both the recomputed `outputHash` and
56
+ the replayed `codeHash` against the snapshot.
57
+ - **`nexart replay --evidence <file>`** writes a `codemode.replay.evidence.v1`
58
+ JSON document (result, expected vs. actual `outputHash`/`codeHash`, renderer,
59
+ and environment fingerprints).
60
+ - **Optional, additive snapshot `environment` fingerprint** (`cliVersion`,
61
+ `sdkVersion`, `protocolVersion`, `node`, `platform`, `arch`, `canvas`) is
62
+ attached to locally-rendered snapshots. It is descriptive metadata only —
63
+ never hashed, never affects pass/fail — and lets `replay`/`verify` warn on
64
+ environment drift. Legacy snapshots omit it and are unaffected.
65
+ - Added `@nexart/codemode-sdk` dependency and `canvas` as an optional
66
+ dependency. Added Deterministic Replay test suite (`replay.test.ts`).
67
+
68
+ ---
69
+
70
+ ## v0.8.1 (2026-05-28)
71
+
72
+ ### Fixes
73
+
74
+ - **Version-drift prevention** — `CLI_VERSION` is now sourced dynamically from `package.json` at load time via `createRequire(import.meta.url)('../package.json')`, replacing the previously hardcoded `'0.8.0'` literal. The `--version` output, CER metadata `version` field, and `verify` report verifier string can no longer drift from the published npm version on subsequent bumps.
75
+ - No API changes. No behavior changes for the current 0.8.x line — the runtime value remains identical, only its source has changed. Added drift-prevention test (Suite Z) that asserts the dynamic resolution matches `package.json`.
76
+
77
+ ---
78
+
79
+ ## v0.8.0 (2026-04-30)
80
+
81
+ ### New Features
82
+
83
+ - **`nexart ai seal [file]`** — local sealing of an AI execution input into a `cer.ai.execution.v1` bundle, fully offline. No node, no network, no API key required.
84
+ Useful for offline workflows, CI pipelines, pre-certification, and piping straight into `nexart ai verify`.
85
+
86
+ All snapshot construction, canonicalization, and certificate hashing is delegated to `createSnapshot()` + `sealCer()` from `@nexart/ai-execution`. The CLI performs input-shape validation only — it never recomputes hashes or canonicalizes JSON. A defensive `hasAttestation()` re-check refuses to ship a misclassified artifact.
87
+
88
+ ```bash
89
+ nexart ai seal execution.json # seal from file → stdout
90
+ cat execution.json | nexart ai seal # seal from stdin
91
+ cat execution.json | nexart ai seal --quiet | nexart ai verify # pipe to verify
92
+ nexart ai seal execution.json --out cer.json # save to file
93
+ ```
94
+
95
+ - **Bundle JSON → stdout**, status banner → **stderr**, so pipes stay clean.
96
+ - **`--quiet` / `-q`** suppresses the stderr banner entirely.
97
+ - **`--out` / `-o`** writes the bundle to disk; stdout output is byte-equal to the file.
98
+ - Status banner: `Local CER created` / `State: Sealed (integrity only)` / `No node attestation present` / `This record is not certified — use \`nexart ai certify\` to attest via a node` / `certificateHash: sha256:...`
99
+ - Help epilogue distinguishes Sealed (this command, integrity only) from Certified (`ai certify`, integrity + node receipt + signature).
100
+ - Sealed bundles verify as `VERIFIED` with Receipt and Envelope reported as `SKIPPED` — that is correct, not a failure (only the Integrity layer applies to local artifacts).
101
+
102
+ ### Verify alignment (Phase 1)
103
+
104
+ - **`nexart ai verify` is now a pure delegation surface** for `verifyAiCerBundleDetailed()` from `@nexart/ai-execution`.
105
+ - The 3 verification layers (`bundleIntegrity`, `nodeSignature`, `receiptConsistency`) are reported independently with SDK reason codes used verbatim.
106
+ - `--json` output now puts the SDK's `CerVerificationResult` at the **top level** (including `verifier: "@nexart/ai-execution"`) and adds an additive `cli` envelope containing `version`, `sdkVersion`, `inputType`, and (for packages) `verifiedInnerCer` / `packageTrustLayersVerified`.
107
+ - The CLI no longer owns any verification semantics: no canonicalization, no hash recomputation, no inner-CER comparison logic.
108
+
109
+ ### Architecture
110
+
111
+ - **Removed** `interface CerBundle` from `src/index.ts` — the canonical bundle type is now `CerAiExecutionBundle` from `@nexart/ai-execution` (a parallel local type would be a drift hazard).
112
+ - **Removed** the dead `sha256hex()` helper from `__tests__/ai.test.ts` — tests no longer recompute CER hashes by hand; all CER cryptographic semantics belong to the SDK.
113
+ - Strict TypeScript audit (`tsc --noUnusedLocals --noUnusedParameters`) is now clean.
114
+ - All `crypto.createHash()` / `sha256()` usage in `src/index.ts` is confined to **Code Mode** paths (runtimeHash, output PNG hash, separate wire protocol `1.2.0`). Zero CER hashing remains in the CLI.
115
+
116
+ ### Compatibility
117
+
118
+ - Fully backward-compatible. All pre-existing commands (`run`, `replay`, `verify` (Code Mode), `ai create`, `ai certify`, `ai project-verify`) are unchanged in behavior.
119
+ - The `--json` output for `ai verify` is **shape-evolved**, not breaking: prior fields are preserved at the top level (now sourced from the SDK result verbatim) and prior CLI-only fields (`inputType`, `verifiedInnerCer`, `packageTrustLayersVerified`) have moved into the new additive `cli` envelope. Consumers that read top-level fields keep working; consumers that read the moved fields need to read them from `cli.*`.
120
+ - The `verifier` field at the top level of `ai verify --json` changes from `"@nexart/cli"` to `"@nexart/ai-execution"`, reflecting that verification semantics now live in the SDK.
121
+ - `ai project-verify --json` is intentionally **out of scope** for this alignment — it continues to emit `verifier: "@nexart/cli"` and its own envelope shape (the underlying verification logic is still fully delegated to `verifyProjectBundle()`). Aligning this is a candidate for a future release.
122
+
123
+ ### Dependencies
124
+
125
+ - Bumped `@nexart/ai-execution` from `^0.13.0` to `^0.16.1` (brings `createSnapshot`, `sealCer`, layered `verifyAiCerBundleDetailed`, and stable reason codes).
126
+
127
+ ### Tests
128
+
129
+ New test group:
130
+
131
+ | Group | What it covers |
132
+ |-------|---------------|
133
+ | **X** | `aiSealCommand` — local CER sealing (12 assertions): X1 bundle shape (`bundleType`, `version`, `certificateHash`, `createdAt`, `snapshot`), X2 no-attestation invariant, X3 stderr banner content, X4 `--quiet` silences stderr, X5 `--out` byte-equal to stdout, X6 round-trip `seal → verify` produces `VERIFIED` with Integrity `PASS` and Receipt+Envelope `SKIPPED`, X7 signals binding into `certificateHash`, X8 tamper detection (`BUNDLE_HASH_MISMATCH`), X9 missing-required-field rejection, X10 array input rejection, X11 malformed `parameters` rejection, X12 determinism (same input → same `certificateHash`) |
134
+
135
+ **Total: 74 tests across 22 suites, all passing.**
136
+
137
+ ---
138
+
139
+ ## v0.7.0 (2026-04-06)
140
+
141
+ ### New Features
142
+
143
+ - **`nexart ai project-verify [file]`** — local verification of `cer.project.bundle.v1` artifacts.
144
+ No network required. Accepts a file path or stdin. Delegates all verification logic to `verifyProjectBundle()` from `@nexart/ai-execution`, which is the canonical source of truth. The CLI owns the command surface, input handling, and output formatting only.
145
+
146
+ `@nexart/ai-execution` is now a declared runtime dependency of `@nexart/cli`.
147
+
148
+ Checks performed (in order, via `@nexart/ai-execution`):
149
+ 1. `bundleType` discriminant: must be `cer.project.bundle.v1`
150
+ 2. Required field presence: `version`, `protocolVersion`, `projectBundleId`, `projectTitle`, `startedAt`, `completedAt`, `integrity` block shape
151
+ 3. `stepRegistry` is a non-empty array
152
+ 4. `embeddedBundles` is a plain object keyed by `stepId`
153
+ 5. `totalSteps` consistency: must equal `stepRegistry.length`
154
+ 6. Per-step: embedded bundle exists for every registry entry; `executionId` and `certificateHash` match; inner CER `certificateHash` is independently valid
155
+ 7. `integrity.projectHash` is recomputed from all material project metadata and compared to the declared value
156
+
157
+ Exit code: `0` on `VERIFIED`, `1` on `FAILED` or structural error.
158
+
159
+ - **`--json` flag** on `nexart ai project-verify`
160
+ Prints a machine-readable `ProjectVerifyResult` object to stdout:
161
+ ```json
162
+ {
163
+ "status": "VERIFIED",
164
+ "bundleType": "cer.project.bundle.v1",
165
+ "projectBundleId": "...",
166
+ "projectTitle": "...",
167
+ "totalSteps": 2,
168
+ "passedSteps": 2,
169
+ "failedSteps": 0,
170
+ "projectHashValid": true,
171
+ "checks": {
172
+ "structuralValid": true,
173
+ "projectHashIntegrity": "PASS",
174
+ "stepIntegrity": "PASS"
175
+ },
176
+ "steps": [ ... ],
177
+ "errors": [],
178
+ "verifiedAt": "...",
179
+ "verifier": "@nexart/cli"
180
+ }
181
+ ```
182
+
183
+ ### Architecture
184
+
185
+ `nexart ai project-verify` follows the same CLI ownership model as `nexart ai verify`:
186
+ - The CLI owns: input routing, argument parsing, discriminant check, and output formatting.
187
+ - `@nexart/ai-execution` owns: project bundle schema, project hash computation, per-step CER verification semantics, and `ProjectBundleVerifyResult` type.
188
+ - The CLI does **not** duplicate or shadow any of `@nexart/ai-execution`'s project bundle verification logic.
189
+
190
+ ### Compatibility
191
+
192
+ - Fully backward-compatible. All existing commands (`create`, `certify`, `verify`) are unchanged.
193
+ - `nexart ai verify` continues to handle `cer.ai.execution.v1` bundles and CER packages. Project bundles require the new `nexart ai project-verify` command.
194
+ - The `ai` group's `demandCommand` hint is updated to include `project-verify` in the listed subcommands.
195
+
196
+ ### Dependencies
197
+
198
+ - **Added**: `@nexart/ai-execution@^0.13.0` as a runtime dependency.
199
+
200
+ ### Tests
201
+
202
+ New test group:
203
+
204
+ | Group | What it covers |
205
+ |-------|---------------|
206
+ | **W** | `aiProjectVerifyCommand` — project bundle verification (9 assertions): W1 PASS human-readable via file, W2 PASS --json with full field checks, W3 FAIL wrong bundleType, W4 FAIL tampered projectHash (human-readable), W5 FAIL tampered projectHash (--json), W6 FAIL inner CER certificateHash tampered (step-level failure), W7 PASS with all optional fields (projectGoal, projectSummary, appName, frameworkName, tags), W8 FAIL totalSteps mismatch, W9 PASS via stdin |
207
+
208
+ Test fixtures for the W group use `createProjectBundle` and `sealCer` from `@nexart/ai-execution` to build fully valid `cer.project.bundle.v1` and `cer.ai.execution.v1` artifacts, ensuring the tests exercise the same verification path as the published package.
209
+
210
+ **Total: 78 tests, all passing (groups A–W).**
211
+
212
+ ---
213
+
214
+ ## v0.6.0 (2026-03-25)
215
+
216
+ ### New Features
217
+
218
+ - **CER package support in `nexart ai verify`** (v0.6.0)
219
+ `nexart ai verify` now accepts both raw CER bundles and **CER packages** — envelope objects with the shape `{ cer: <bundle>, receipt?, signature?, ... }`.
220
+ Detection is automatic; no extra flags are needed.
221
+ - If the input has a top-level `bundleType === 'cer.ai.execution.v1'`, it is treated as a raw bundle (behavior unchanged from v0.5.0).
222
+ - If the input has a `cer` field whose inner `bundleType === 'cer.ai.execution.v1'`, it is treated as a CER package. Only the inner bundle is verified; receipt, signature, and other envelope fields are not verified by this command.
223
+
224
+ - **`isCerPackageInput` internal helper**
225
+ Mirrors the detection logic of `isCerPackage()` from `@nexart/ai-execution` inline. The CLI remains self-contained with no runtime dependency on the SDK.
226
+
227
+ - **`inputType` field in `--json` output**
228
+ All `nexart ai verify --json` output now includes `"inputType": "bundle"` or `"inputType": "package"` (additive, backward-compatible).
229
+
230
+ - **Package-specific `--json` output fields**
231
+ When verifying a CER package, the JSON output additionally includes:
232
+ - `"verifiedInnerCer": true` — confirms the inner `cer` bundle was the verification target.
233
+ - `"packageTrustLayersVerified": false` — signals that receipt/signature/envelope verification is not performed by this command.
234
+
235
+ ### Compatibility
236
+
237
+ - Fully backward-compatible with all existing raw CER bundle inputs.
238
+ - Existing JSON consumers that read `nexart ai verify --json` output will see the new additive `inputType` field; all prior fields are preserved.
239
+ - Compatible with CER packages produced by `@nexart/ai-execution@0.12.0` (`createCerPackage`, `exportCerPackage`).
240
+
241
+ ### Tests
242
+
243
+ New test group:
244
+
245
+ | Group | What it covers |
246
+ |-------|---------------|
247
+ | **V** | `aiVerifyCommand` — CER package input (8 assertions): PASS human-readable via stdin/file, PASS JSON with package fields, FAIL tampered inner CER (human-readable + JSON), PASS with receipt/signature envelope, backward-compat `inputType: "bundle"` for raw bundles, non-package `cer` field falls through to error path |
248
+
249
+ **Total: 69 tests, all passing (groups A–V).**
250
+
251
+ ---
252
+
253
+ ## v0.5.0 (2026-03-17)
254
+
255
+ ### New Features
256
+
257
+ - **`--signals-file` flag** on `nexart ai create` and `nexart ai certify`.
258
+ Reads a JSON array of context signals from the given file and merges them into the request body as `{ signals: [...] }`. The node API embeds the signals in the CER under `context.signals` and includes them in the `certificateHash`.
259
+
260
+ - **`loadSignalsFile(path)` export** — new utility that reads, parses, and validates a signals JSON array from disk. Exits with a clear error message if the file is missing, contains invalid JSON, or is not a JSON array.
261
+
262
+ ### Bug Fixes
263
+
264
+ - **Critical: `computeBundleHash` now includes `context` in the protected set** when present (non-null/non-undefined).
265
+ Previously, `nexart ai verify` on a v0.11.0+ CER containing `context.signals` would always fail with a spurious `BUNDLE_HASH_MISMATCH` because the hash was computed without `context`. The fix aligns the CLI's local hash computation with `@nexart/ai-execution@0.11.0` semantics.
266
+ **Backward compatible:** bundles without `context` continue to produce the same hash as before.
267
+
268
+ ### Compatibility
269
+
270
+ - Compatible with `@nexart/ai-execution@0.11.0` CER format (optional `context.signals`).
271
+ - `nexart ai verify` handles both plain CERs (no `context`) and CERs with `context.signals` without any extra flags.
272
+
273
+ ### Tests
274
+
275
+ New test groups:
276
+
277
+ | Group | What it covers |
278
+ |-------|---------------|
279
+ | **R** | `computeBundleHash` with context signals (7 assertions) |
280
+ | **S** | `aiVerifyCommand` — signals-containing CER bundle PASS/FAIL (4 assertions) |
281
+ | **T** | `loadSignalsFile` helper — valid array, missing file, invalid JSON, non-array, empty array (5 assertions) |
282
+ | **U** | `aiCreateCommand` / `aiCertifyCommand` `--signals-file` merges signals into request body (4 assertions) |
283
+
284
+ ---
285
+
286
+ ## v0.4.0 (2026-03-10)
287
+
288
+ - `nexart ai create` — `--out` flag saves returned CER bundle to file (group P)
289
+ - `nexart ai certify` — 401/422 error handling (group Q)
290
+ - `callNodeApi` — correctly omits `Authorization` header when no API key provided (group M)
291
+ - All AI command stdinReader tests (groups N, O)
292
+
293
+ ## v0.3.0 (2026-03-01)
294
+
295
+ - Initial `nexart ai create`, `nexart ai certify`, `nexart ai verify` commands
296
+ - `canonicalJson` + `computeBundleHash` (protected set: `bundleType`, `version`, `createdAt`, `snapshot`)
297
+ - `hasAttestation` helper
298
+ - Test groups A–L covering core AI certification commands
package/README.md CHANGED
@@ -1,7 +1,23 @@
1
- # @nexart/cli v0.8.0
1
+ # @nexart/cli v0.10.0
2
2
 
3
3
  Command-line interface for NexArt — run, replay, and verify deterministic generative art, plus AI execution certification commands.
4
4
 
5
+ ## protocolVersion: 1.2.0 vs 1.3.0
6
+
7
+ Verified records carry a `protocolVersion` that selects how their bytes are
8
+ canonicalised before hashing — `ai verify` surfaces it in both human-readable and
9
+ `--json` output (`cli.protocolVersion`).
10
+
11
+ - **1.2.0 (legacy, default):** the original NexArt canonicalisation (`nexart-v1`),
12
+ fully supported and **frozen**.
13
+ - **1.3.0 (stranger-verifiable):** RFC 8785 (JSON Canonicalization Scheme, `jcs-v1`).
14
+ **Required for independent / external verification** by a third party who does not
15
+ run our exact code.
16
+
17
+ A record with an unknown or unsupported `protocolVersion` fails closed (verify code
18
+ `SCHEMA_ERROR`, reason code `SCHEMA_VERSION_UNSUPPORTED`, exit 1) — it is never
19
+ mis-verified. The CLI adds no fallback or default.
20
+
5
21
  ## Installation
6
22
 
7
23
  ```bash
@@ -130,10 +146,23 @@ nexart run sketch.js --seed 12345 --out render.png
130
146
  # With code embedded for standalone verify/replay
131
147
  nexart run sketch.js --seed 12345 --include-code
132
148
 
133
- # Local mode (placeholder PNG, no auth required)
134
- nexart run sketch.js --renderer local
149
+ # Local deterministic mode (no auth required; requires the optional `canvas` dependency)
150
+ nexart run sketch.js --renderer local --include-code
135
151
  ```
136
152
 
153
+ > **Deterministic Replay (local renderer).** With `--renderer local`, the CLI
154
+ > renders the sketch using the same canonical CodeMode render core as the SDK
155
+ > and hashes the encoded PNG bytes — the identical `outputHash` definition used
156
+ > by the remote renderer. For **controlled CodeMode / canonical render**
157
+ > snapshots this is byte-reproducible **within the same runtime and render
158
+ > libraries** (Node version, platform/arch, and `canvas` native libs). To make
159
+ > that reproducibility checkable, local snapshots additionally embed an
160
+ > optional, additive `environment` fingerprint (see below). Reproducibility
161
+ > across *different* environments is not guaranteed; `replay`/`verify` will warn
162
+ > when the environment differs. The local renderer requires the optional
163
+ > `canvas` package and its native libraries — if they are missing, local
164
+ > rendering fails with a clear, actionable error (never a silent placeholder).
165
+
137
166
  **Options:**
138
167
  | Flag | Default | Description |
139
168
  |------|---------|-------------|
@@ -152,32 +181,74 @@ nexart run sketch.js --renderer local
152
181
  - `render.png` — The rendered image
153
182
  - `render.snapshot.json` — Snapshot for replay/verify
154
183
 
184
+ **Snapshot `environment` field (additive, optional).** Snapshots produced with
185
+ `--renderer local` include an `environment` object describing the render
186
+ environment (`cliVersion`, `sdkVersion`, `protocolVersion`, `node`, `platform`,
187
+ `arch`, `canvas`). It is **descriptive metadata only** — it is never hashed and
188
+ never affects pass/fail. It exists so `replay`/`verify` can warn when the replay
189
+ environment differs from the capture environment. Legacy snapshots (and
190
+ remote-rendered snapshots) omit this field and continue to verify and replay
191
+ exactly as before.
192
+
155
193
  ### verify
156
194
 
157
195
  Check that a snapshot produces the expected output:
158
196
 
159
197
  ```bash
198
+ # Verify against the canonical remote renderer (default)
160
199
  nexart verify render.snapshot.json
161
200
 
201
+ # Verify locally with the deterministic CodeMode renderer (requires `canvas`)
202
+ nexart verify render.snapshot.json --renderer local
203
+
162
204
  # With external code file
163
205
  nexart verify render.snapshot.json --code sketch.js
164
206
  ```
165
207
 
208
+ The local path performs a **real render** (no placeholder) — a missing `canvas`
209
+ dependency fails clearly rather than producing a false pass.
210
+
166
211
  **Exit codes:**
167
212
  - `0` — PASS (hashes match)
168
213
  - `1` — FAIL (hashes differ or error)
169
214
 
170
215
  ### replay
171
216
 
172
- Re-execute from a snapshot:
217
+ Re-execute from a snapshot, compare the result against the snapshot, and report
218
+ `PASS`/`FAIL`:
173
219
 
174
220
  ```bash
175
- nexart replay render.snapshot.json --out replay.png
221
+ # Deterministic local replay with PASS/FAIL evidence
222
+ nexart replay render.snapshot.json --renderer local --out replay.png \
223
+ --evidence replay.evidence.json
176
224
 
177
225
  # With external code file
178
226
  nexart replay render.snapshot.json --code sketch.js --out replay.png
179
227
  ```
180
228
 
229
+ `replay` recomputes the output hash and compares it to the snapshot's
230
+ `outputHash`, and independently checks that the replayed code matches the
231
+ snapshot's `codeHash`. The overall result is `PASS` only when **both** match.
232
+
233
+ **Options:**
234
+ | Flag | Default | Description |
235
+ |------|---------|-------------|
236
+ | `--out, -o` | replay.png | Output PNG path |
237
+ | `--code, -c` | — | Code file (if not embedded in snapshot) |
238
+ | `--renderer` | remote | `remote` or `local` (deterministic CodeMode renderer) |
239
+ | `--endpoint` | env/localhost:5000 | Remote renderer URL |
240
+ | `--evidence` | — | Write a JSON replay-evidence file (PASS/FAIL + hash comparison) |
241
+ | `--api-key` | env | API key for authentication |
242
+
243
+ **Exit codes:**
244
+ - `0` — PASS (output and code match the snapshot)
245
+ - `1` — FAIL (output or code differs, or render error)
246
+
247
+ When `--evidence <file>` is given, `replay` writes a
248
+ `codemode.replay.evidence.v1` JSON document recording the `PASS`/`FAIL` result,
249
+ the expected vs. actual `outputHash` and `codeHash`, the renderer used, and the
250
+ snapshot vs. current `environment` fingerprints (with any differing fields).
251
+
181
252
  ---
182
253
 
183
254
  ## AI Certification Commands (`nexart ai`)
@@ -400,7 +471,7 @@ bundleIntegrity: FAIL
400
471
  "verifiedAt": "2026-04-30T12:00:00.000Z",
401
472
  "verifier": "@nexart/ai-execution",
402
473
  "cli": {
403
- "version": "0.8.0",
474
+ "version": "0.9.0",
404
475
  "sdkVersion": "0.16.1",
405
476
  "inputType": "bundle"
406
477
  }
@@ -422,7 +493,7 @@ bundleIntegrity: FAIL
422
493
  "verifiedAt": "2026-04-30T12:00:00.000Z",
423
494
  "verifier": "@nexart/ai-execution",
424
495
  "cli": {
425
- "version": "0.8.0",
496
+ "version": "0.9.0",
426
497
  "sdkVersion": "0.16.1",
427
498
  "inputType": "package",
428
499
  "verifiedInnerCer": true,
@@ -433,10 +504,29 @@ bundleIntegrity: FAIL
433
504
 
434
505
  The top-level fields (`status`, `checks`, `reasonCodes`, `certificateHash`, `bundleType`, `verifiedAt`, `verifier`) are the SDK's `CerVerificationResult` verbatim. The additive `cli` envelope carries CLI-only metadata (`version`, `sdkVersion`, `inputType`, and package-specific flags when applicable).
435
506
 
507
+ **Enterprise trust layers (`--anchors`, `--timestamps`):** these flags opt in to the
508
+ **non-evidentiary** secondary-proof layers. They are reported separately and **never** change the
509
+ base verification result or the exit code — they cannot turn a `VERIFIED` bundle into `FAILED` or
510
+ vice-versa.
511
+
512
+ ```bash
513
+ # Report anchor status (VALID / INVALID / NOT PRESENT)
514
+ nexart ai verify cer.json --anchors
515
+
516
+ # Report anchor + trusted timestamp status with a trust summary
517
+ nexart ai verify cer.json --anchors --timestamps
518
+ ```
519
+
520
+ When enabled, the human-readable output adds a `Layers:` block (Anchoring / Timestamp lines) and a
521
+ `Trust summary` block (Signature / Anchors / Timestamp / Protocol Version); the JSON output adds
522
+ top-level `anchorStatus` and `timestampStatus` objects (`{ checked, valid, errors? }`).
523
+
436
524
  **Options:**
437
525
  | Flag | Default | Description |
438
526
  |------|---------|-------------|
439
527
  | `--json` | false | Machine-readable JSON output |
528
+ | `--anchors` | false | Verify and report anchor status (non-evidentiary; does not affect exit code) |
529
+ | `--timestamps` | false | Verify and report trusted-timestamp status + trust summary (non-evidentiary) |
440
530
 
441
531
  **Exit codes:**
442
532
  - `0` — PASS (inner bundle hash matches)
@@ -444,6 +534,47 @@ The top-level fields (`status`, `checks`, `reasonCodes`, `certificateHash`, `bun
444
534
 
445
535
  ---
446
536
 
537
+ ### `nexart ai attach-anchor`
538
+
539
+ Attach an external anchor (transparency log / TSA / blockchain) to a CER bundle. This is an
540
+ append-only, non-evidentiary operation — it does not re-hash or re-sign the bundle.
541
+
542
+ ```bash
543
+ # Attach an anchor and verify it in one pipe
544
+ nexart ai attach-anchor cer.json anchor.json | nexart ai verify --anchors
545
+ ```
546
+
547
+ ---
548
+
549
+ ### `nexart export-verification`
550
+
551
+ Produce a self-contained verification package that a third party can verify **offline**, without the
552
+ NexArt node or this SDK. It is read-only — nothing is re-hashed or re-signed.
553
+
554
+ ```bash
555
+ # Print a verification package to stdout
556
+ nexart export-verification cer.json
557
+
558
+ # Resolve the signing public key from a node-keys document and save the package
559
+ nexart export-verification cer.json --node-keys keys.json --out pkg.json
560
+
561
+ # From stdin
562
+ cat cer.json | nexart export-verification
563
+ ```
564
+
565
+ The package contains `{ bundle, publicKey, verificationInstructions }`, where
566
+ `verificationInstructions` lists the `protocolVersion`, `canonicalMode`, `certificateHash`,
567
+ `signatureAlgorithm`, the present layer flags, and ordered verification steps.
568
+
569
+ **Options:**
570
+ | Flag | Default | Description |
571
+ |------|---------|-------------|
572
+ | `--out, -o` | — | Save the package to this file (otherwise printed to stdout) |
573
+ | `--node-keys` | — | Path to a `NodeKeysDocument` JSON to resolve the signing public key |
574
+ | `--public-key` | — | Provide the public key directly (takes precedence over `--node-keys`) |
575
+
576
+ ---
577
+
447
578
  ### `nexart ai project-verify` (v0.7.0+)
448
579
 
449
580
  Verify a **project bundle** (`cer.project.bundle.v1`) locally. No network required. Delegates all verification logic to `verifyProjectBundle()` from `@nexart/ai-execution` — the canonical source of truth for project bundle semantics. The CLI provides the command surface and output formatting.
package/dist/index.d.ts CHANGED
@@ -145,32 +145,44 @@ export declare function aiCertifyCommand(file: string | undefined, opts: {
145
145
  apiKey?: string;
146
146
  signalsFile?: string;
147
147
  }, fetchFn?: typeof fetch, stdinReader?: () => Promise<string>): Promise<void>;
148
- /**
149
- * nexart ai verify — Read a CER bundle or CER package JSON from file or stdin,
150
- * delegate verification to verifyAiCerBundleDetailed() from @nexart/ai-execution,
151
- * and report the protocol layer results.
152
- *
153
- * The CLI does ZERO crypto here. It only:
154
- * 1. parses input
155
- * 2. unwraps a CER package to its inner CER bundle (via SDK's isCerPackage / getCerFromPackage)
156
- * 3. calls verifyAiCerBundleDetailed(bundle)
157
- * 4. formats the SDK's CerVerificationResult for human / JSON output
158
- *
159
- * The three SDK protocol layers are reported INDEPENDENTLY (no aggregation):
160
- * - bundleIntegrity (Integrity layer: certificate hash recomputation)
161
- * - nodeSignature (Receipt layer: node attestation signature validity)
162
- * - receiptConsistency (Envelope layer: bundle/receipt envelope consistency)
163
- *
164
- * Each layer is PASS / FAIL / SKIPPED. SDK reason codes are passed through
165
- * unmodified. Exit code is driven by the SDK's `status` field (VERIFIED → 0,
166
- * anything else → 1).
167
- *
168
- * --json prints the SDK's CerVerificationResult verbatim, plus a minimal
169
- * `cli` envelope describing input handling. The SDK fields are never rewritten
170
- * or reinterpreted.
171
- */
172
148
  export declare function aiVerifyCommand(file: string | undefined, opts: {
173
149
  json: boolean;
150
+ anchors?: boolean;
151
+ timestamps?: boolean;
152
+ }, stdinReader?: () => Promise<string>): Promise<void>;
153
+ /**
154
+ * nexart ai attach-anchor — Attach an external anchor (proof of existence) to a
155
+ * CER bundle and emit the updated bundle.
156
+ *
157
+ * Reads a CER bundle JSON and an anchor JSON, calls the SDK's pure
158
+ * `attachAnchor()` (new object, no mutation), and writes the result to stdout
159
+ * (or --out). The certificateHash is GUARANTEED unchanged: anchors are
160
+ * non-evidentiary and never participate in hashing or signing.
161
+ *
162
+ * The SDK does NOT generate anchors — they come from external systems (e.g. the
163
+ * attestation node's transparency log). This command only attaches a supplied
164
+ * anchor.
165
+ */
166
+ export declare function aiAttachAnchorCommand(bundleFile: string, anchorFile: string, opts: {
167
+ out?: string;
168
+ }): Promise<void>;
169
+ /**
170
+ * nexart export-verification — Produce a self-contained verification package for
171
+ * a CER bundle so a third party can verify it INDEPENDENTLY (no NexArt node, no
172
+ * this SDK required).
173
+ *
174
+ * Delegates entirely to the SDK's `exportVerificationPackage()`. The CLI only
175
+ * handles file/stdin I/O and (optionally) supplies key material:
176
+ * --public-key <b64url> raw Ed25519 public key (highest priority)
177
+ * --node-keys <file> a NodeKeysDocument JSON to select the receipt's key from
178
+ * --out <file> write the package to a file (default: stdout)
179
+ *
180
+ * Read-only and additive: never re-hashes, re-signs, or mutates the bundle.
181
+ */
182
+ export declare function exportVerificationCommand(file: string | undefined, opts: {
183
+ out?: string;
184
+ nodeKeys?: string;
185
+ publicKey?: string;
174
186
  }, stdinReader?: () => Promise<string>): Promise<void>;
175
187
  /**
176
188
  * nexart ai project-verify — Read a cer.project.bundle.v1 JSON from file or stdin,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAYH,OAAO,EAGL,cAAc,IAAI,iBAAiB,EAYpC,MAAM,sBAAsB,CAAC;AAgmB9B;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAerD;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,WAAW,GAAE,MAAM,OAAO,CAAC,MAAM,CAAiB,GACjD,OAAO,CAAC,OAAO,CAAC,CA0BlB;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,OAAO,EACb,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,GAAE,OAAO,KAAwB,GACvC,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAoB5C;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,0BAAoB,CAAC;AAEhD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,EAAE,CAwB9D;AAuDD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE;IACJ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,EACD,WAAW,GAAE,MAAM,OAAO,CAAC,MAAM,CAAiB,GACjD,OAAO,CAAC,IAAI,CAAC,CAuCf;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE;IACJ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,EACD,OAAO,GAAE,OAAO,KAAwB,EACxC,WAAW,GAAE,MAAM,OAAO,CAAC,MAAM,CAAiB,GACjD,OAAO,CAAC,IAAI,CAAC,CA8Cf;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE;IACJ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,EACD,OAAO,GAAE,OAAO,KAAwB,EACxC,WAAW,GAAE,MAAM,OAAO,CAAC,MAAM,CAAiB,GACjD,OAAO,CAAC,IAAI,CAAC,CA8Df;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,EACvB,WAAW,GAAE,MAAM,OAAO,CAAC,MAAM,CAAiB,GACjD,OAAO,CAAC,IAAI,CAAC,CA0Ef;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,EACvB,WAAW,GAAE,MAAM,OAAO,CAAC,MAAM,CAAiB,GACjD,OAAO,CAAC,IAAI,CAAC,CAwBf"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}