@nexart/ai-execution 0.4.2 → 0.5.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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @nexart/ai-execution v0.4.2
1
+ # @nexart/ai-execution v0.5.0
2
2
 
3
3
  Tamper-evident records and Certified Execution Records (CER) for AI operations.
4
4
 
@@ -192,6 +192,8 @@ type AttestationReceipt = {
192
192
  protocolVersion: string;
193
193
  nodeId?: string;
194
194
  attestedAt?: string; // ISO 8601
195
+ attestorKeyId?: string; // kid of the signing key (v0.5.0+)
196
+ signatureB64Url?: string; // base64url Ed25519 signature (v0.5.0+)
195
197
  };
196
198
  ```
197
199
 
@@ -221,6 +223,49 @@ const receipt = getAttestationReceipt(bundle); // null if not yet attested
221
223
  - `attestIfNeeded(bundle, options)` — skips the node call if a valid receipt is already present; prevents double-attestation
222
224
  - `certifyAndAttestDecision(params, options)` — recommended one-call integration: `certifyDecision` + `attest` + normalized receipt
223
225
 
226
+ ### Signed Receipt Verification (v0.5.0+)
227
+
228
+ After a node signs the receipt, verify it offline without a round-trip:
229
+
230
+ ```ts
231
+ import {
232
+ verifyNodeReceiptSignature,
233
+ verifyBundleAttestation,
234
+ fetchNodeKeys,
235
+ selectNodeKey,
236
+ } from '@nexart/ai-execution';
237
+
238
+ // One-call: fetches node keys, selects correct key, verifies Ed25519 signature
239
+ const result = await verifyBundleAttestation(bundle, {
240
+ nodeUrl: 'https://my-node.example.com',
241
+ });
242
+ // result.ok === true → signature is valid
243
+ // result.code → CerVerifyCode enum value
244
+ // result.details → string[] with failure explanation (when ok=false)
245
+ ```
246
+
247
+ **Verify against a specific key directly:**
248
+
249
+ ```ts
250
+ const result = await verifyNodeReceiptSignature({
251
+ receipt: { attestationId: '...', certificateHash: 'sha256:...', ... },
252
+ signatureB64Url: 'aDEKyu...Q',
253
+ key: { jwk: { kty: 'OKP', crv: 'Ed25519', x: '<base64url pubkey>' } },
254
+ // or: key: { rawB64Url: '<base64url raw 32 bytes>' }
255
+ });
256
+ ```
257
+
258
+ **Failure codes:**
259
+
260
+ | Code | Meaning |
261
+ |---|---|
262
+ | `ATTESTATION_MISSING` | No signed receipt in bundle |
263
+ | `ATTESTATION_KEY_NOT_FOUND` | kid not found in node keys document |
264
+ | `ATTESTATION_INVALID_SIGNATURE` | Ed25519 signature did not verify |
265
+ | `ATTESTATION_KEY_FORMAT_UNSUPPORTED` | Key cannot be decoded (wrong crv, no fields, etc.) |
266
+
267
+ **Node keys document** is fetched from `{nodeUrl}/.well-known/nexart-node.json`. See `SPEC.md` for the full shape.
268
+
224
269
  ### Sanitization and Redaction
225
270
 
226
271
  `sanitizeForAttestation(bundle)` returns a JSON-safe deep clone:
@@ -282,6 +327,10 @@ Fixtures at `fixtures/vectors/` and `fixtures/golden/`. Cross-language implement
282
327
  | `certifyAndAttestDecision(params, options)` | One-call: certifyDecision + attest + receipt |
283
328
  | `attestIfNeeded(bundle, options)` | Attest only if no receipt already present |
284
329
  | `getAttestationReceipt(bundle)` | Extract normalized `AttestationReceipt` or `null` |
330
+ | `verifyNodeReceiptSignature(params)` | Verify an Ed25519-signed receipt offline (v0.5.0+) |
331
+ | `fetchNodeKeys(nodeUrl)` | Fetch `NodeKeysDocument` from `/.well-known/nexart-node.json` (v0.5.0+) |
332
+ | `selectNodeKey(doc, kid?)` | Select a key from a `NodeKeysDocument` by kid or activeKid (v0.5.0+) |
333
+ | `verifyBundleAttestation(bundle, options)` | One-call offline attestation verification (v0.5.0+) |
285
334
  | `sanitizeForAttestation(bundle)` | Remove `undefined` keys, reject BigInt/functions/symbols |
286
335
  | `hasAttestation(bundle)` | Check if bundle already has attestation fields |
287
336
  | `exportCer(bundle)` | Serialize to canonical JSON string |
@@ -302,6 +351,10 @@ Fixtures at `fixtures/vectors/` and `fixtures/golden/`. Cross-language implement
302
351
  | `SCHEMA_ERROR` | Wrong bundleType/version, missing snapshot, non-finite parameters, etc. |
303
352
  | `CANONICALIZATION_ERROR` | `toCanonicalJson` threw during verification |
304
353
  | `UNKNOWN_ERROR` | Catch-all for unclassified failures |
354
+ | `ATTESTATION_MISSING` | No signed receipt found in bundle (v0.5.0+) |
355
+ | `ATTESTATION_KEY_NOT_FOUND` | kid not found in node keys document (v0.5.0+) |
356
+ | `ATTESTATION_INVALID_SIGNATURE` | Ed25519 signature did not verify (v0.5.0+) |
357
+ | `ATTESTATION_KEY_FORMAT_UNSUPPORTED` | Key cannot be decoded (v0.5.0+) |
305
358
 
306
359
  Priority when multiple failures exist: `CANONICALIZATION_ERROR` > `SCHEMA_ERROR` > `INVALID_SHA256_FORMAT` > `CERTIFICATE_HASH_MISMATCH` > `INPUT_HASH_MISMATCH` > `OUTPUT_HASH_MISMATCH` > `SNAPSHOT_HASH_MISMATCH` > `UNKNOWN_ERROR`.
307
360
 
@@ -324,7 +377,8 @@ Priority when multiple failures exist: `CANONICALIZATION_ERROR` > `SCHEMA_ERROR`
324
377
  | v0.3.0 | Attestation hardening (hash validation, timeout), `verify` alias, `CerAttestationError.details`, release hygiene |
325
378
  | v0.4.0 | Dual ESM/CJS build, `sanitizeForAttestation`, `hasAttestation`, auto-sanitize in `attest()`, fixed `ERR_PACKAGE_PATH_NOT_EXPORTED` |
326
379
  | v0.4.1 | Verification reason codes (`CerVerifyCode`), `code` + `details` on `VerificationResult`, README provenance wording tightened |
327
- | **v0.4.2** | `AttestationReceipt`, `getAttestationReceipt`, `certifyAndAttestDecision`, `attestIfNeeded` |
380
+ | v0.4.2 | `AttestationReceipt`, `getAttestationReceipt`, `certifyAndAttestDecision`, `attestIfNeeded` |
381
+ | **v0.5.0** | Ed25519 signed receipt verification: `verifyNodeReceiptSignature`, `verifyBundleAttestation`, `fetchNodeKeys`, `selectNodeKey`; new attestation `CerVerifyCode` entries; `SPEC.md`; `NodeKeysDocument`, `SignedAttestationReceipt`, `NodeReceiptVerifyResult` types |
328
382
  | v1.0.0 | Planned: API stabilization, freeze public API surface |
329
383
 
330
384
  ## Releasing