@nexart/ai-execution 0.4.1 → 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 +103 -4
- package/dist/index.cjs +1203 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -3
- package/dist/index.d.ts +39 -3
- package/dist/index.mjs +1196 -10
- package/dist/index.mjs.map +1 -1
- package/dist/providers/anthropic.cjs +1 -1
- package/dist/providers/anthropic.cjs.map +1 -1
- package/dist/providers/anthropic.d.cts +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.mjs +1 -1
- package/dist/providers/anthropic.mjs.map +1 -1
- package/dist/providers/openai.cjs +1 -1
- package/dist/providers/openai.cjs.map +1 -1
- package/dist/providers/openai.d.cts +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.mjs +1 -1
- package/dist/providers/openai.mjs.map +1 -1
- package/dist/providers/wrap.cjs +1 -1
- package/dist/providers/wrap.cjs.map +1 -1
- package/dist/providers/wrap.d.cts +1 -1
- package/dist/providers/wrap.d.ts +1 -1
- package/dist/providers/wrap.mjs +1 -1
- package/dist/providers/wrap.mjs.map +1 -1
- package/dist/{types-342Snbrb.d.cts → types-BjEqrksn.d.cts} +50 -1
- package/dist/{types-342Snbrb.d.ts → types-BjEqrksn.d.ts} +50 -1
- package/fixtures/attestation/keys-v1.json +18 -0
- package/fixtures/attestation/receipt-v1.json +10 -0
- package/fixtures/attestation/receipt-v1.pub +1 -0
- package/fixtures/attestation/receipt-v1.sig +1 -0
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @nexart/ai-execution v0.
|
|
1
|
+
# @nexart/ai-execution v0.5.0
|
|
2
2
|
|
|
3
3
|
Tamper-evident records and Certified Execution Records (CER) for AI operations.
|
|
4
4
|
|
|
@@ -21,7 +21,7 @@ These records can be verified offline to detect any post-hoc modification and pr
|
|
|
21
21
|
|
|
22
22
|
## Compatibility Guarantees
|
|
23
23
|
|
|
24
|
-
- **v0.1.0, v0.2.0, and v0.3.0 bundles verify forever.** Any CER bundle produced by any prior version will pass `verify()` in v0.4.
|
|
24
|
+
- **v0.1.0, v0.2.0, and v0.3.0 bundles verify forever.** Any CER bundle produced by any prior version will pass `verify()` in v0.4.2 and all future versions.
|
|
25
25
|
- **Hashing rules are frozen for `cer.ai.execution.v1`.** The canonicalization, SHA-256 computation, and certificate hash inputs (bundleType, version, createdAt, snapshot) are unchanged.
|
|
26
26
|
- **New optional snapshot fields** (runId, stepId, stepIndex, etc.) default to undefined and are excluded from legacy snapshots. They participate in the certificate hash only when present.
|
|
27
27
|
- **Canonicalization is frozen for v1.** Number-to-string conversion uses `JSON.stringify()`, which is consistent across JavaScript engines but does not implement RFC 8785 (JCS) for edge cases like `-0`. If stricter canonicalization is required, it will ship as a new bundle type (`cer.ai.execution.v2`), never as a modification to v1.
|
|
@@ -139,7 +139,7 @@ const restored = importCer(json); // parse + verify (throws on tamper)
|
|
|
139
139
|
| `modelVersion` | Optional | `string \| null` | Defaults to `null` |
|
|
140
140
|
| `parameters.topP` | Optional | `number \| null` | Defaults to `null` |
|
|
141
141
|
| `parameters.seed` | Optional | `number \| null` | Defaults to `null` |
|
|
142
|
-
| `sdkVersion` | Optional | `string \| null` | Defaults to `"0.4.
|
|
142
|
+
| `sdkVersion` | Optional | `string \| null` | Defaults to `"0.4.2"` |
|
|
143
143
|
| `appId` | Optional | `string \| null` | Defaults to `null` |
|
|
144
144
|
| `runId` | Optional | `string \| null` | Workflow run ID |
|
|
145
145
|
| `stepId` | Optional | `string \| null` | Step identifier within a run |
|
|
@@ -180,6 +180,92 @@ Endpoint: `POST {nodeUrl}/api/attest`
|
|
|
180
180
|
|
|
181
181
|
Attestation verifies internal integrity only. It does not re-run the model or validate the correctness of the AI output.
|
|
182
182
|
|
|
183
|
+
### Attestation Receipt
|
|
184
|
+
|
|
185
|
+
After a successful attestation, you get a normalized `AttestationReceipt`:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
type AttestationReceipt = {
|
|
189
|
+
attestationId: string;
|
|
190
|
+
certificateHash: string; // sha256:...
|
|
191
|
+
nodeRuntimeHash: string; // sha256:...
|
|
192
|
+
protocolVersion: string;
|
|
193
|
+
nodeId?: string;
|
|
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+)
|
|
197
|
+
};
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Recommended one-call integration:**
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
import { certifyAndAttestDecision } from '@nexart/ai-execution';
|
|
204
|
+
|
|
205
|
+
const { bundle, receipt } = await certifyAndAttestDecision(params, {
|
|
206
|
+
nodeUrl: 'https://my-node.example.com',
|
|
207
|
+
apiKey: process.env.NODE_API_KEY!,
|
|
208
|
+
});
|
|
209
|
+
// bundle is the sealed CER, receipt is the normalized attestation proof
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Skip re-attestation when already attested:**
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import { attestIfNeeded, getAttestationReceipt } from '@nexart/ai-execution';
|
|
216
|
+
|
|
217
|
+
const { receipt } = await attestIfNeeded(bundle, options);
|
|
218
|
+
// or just read without network call:
|
|
219
|
+
const receipt = getAttestationReceipt(bundle); // null if not yet attested
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- `getAttestationReceipt(bundle)` — extracts a normalized receipt from any supported shape (top-level fields or `bundle.meta.attestation`); returns `null` if required fields are missing, never throws
|
|
223
|
+
- `attestIfNeeded(bundle, options)` — skips the node call if a valid receipt is already present; prevents double-attestation
|
|
224
|
+
- `certifyAndAttestDecision(params, options)` — recommended one-call integration: `certifyDecision` + `attest` + normalized receipt
|
|
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
|
+
|
|
183
269
|
### Sanitization and Redaction
|
|
184
270
|
|
|
185
271
|
`sanitizeForAttestation(bundle)` returns a JSON-safe deep clone:
|
|
@@ -238,6 +324,13 @@ Fixtures at `fixtures/vectors/` and `fixtures/golden/`. Cross-language implement
|
|
|
238
324
|
| Function | Description |
|
|
239
325
|
|---|---|
|
|
240
326
|
| `attest(bundle, options)` | Post CER to canonical node (auto-sanitizes) |
|
|
327
|
+
| `certifyAndAttestDecision(params, options)` | One-call: certifyDecision + attest + receipt |
|
|
328
|
+
| `attestIfNeeded(bundle, options)` | Attest only if no receipt already present |
|
|
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+) |
|
|
241
334
|
| `sanitizeForAttestation(bundle)` | Remove `undefined` keys, reject BigInt/functions/symbols |
|
|
242
335
|
| `hasAttestation(bundle)` | Check if bundle already has attestation fields |
|
|
243
336
|
| `exportCer(bundle)` | Serialize to canonical JSON string |
|
|
@@ -258,6 +351,10 @@ Fixtures at `fixtures/vectors/` and `fixtures/golden/`. Cross-language implement
|
|
|
258
351
|
| `SCHEMA_ERROR` | Wrong bundleType/version, missing snapshot, non-finite parameters, etc. |
|
|
259
352
|
| `CANONICALIZATION_ERROR` | `toCanonicalJson` threw during verification |
|
|
260
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+) |
|
|
261
358
|
|
|
262
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`.
|
|
263
360
|
|
|
@@ -279,7 +376,9 @@ Priority when multiple failures exist: `CANONICALIZATION_ERROR` > `SCHEMA_ERROR`
|
|
|
279
376
|
| v0.2.0 | certifyDecision, RunBuilder, attest, archive, Anthropic, wrapProvider, typed errors, workflow fields |
|
|
280
377
|
| v0.3.0 | Attestation hardening (hash validation, timeout), `verify` alias, `CerAttestationError.details`, release hygiene |
|
|
281
378
|
| v0.4.0 | Dual ESM/CJS build, `sanitizeForAttestation`, `hasAttestation`, auto-sanitize in `attest()`, fixed `ERR_PACKAGE_PATH_NOT_EXPORTED` |
|
|
282
|
-
|
|
|
379
|
+
| v0.4.1 | Verification reason codes (`CerVerifyCode`), `code` + `details` on `VerificationResult`, README provenance wording tightened |
|
|
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 |
|
|
283
382
|
| v1.0.0 | Planned: API stabilization, freeze public API surface |
|
|
284
383
|
|
|
285
384
|
## Releasing
|