@nexart/ai-execution 0.2.0 → 0.3.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 +89 -333
- package/dist/attest.d.ts.map +1 -1
- package/dist/attest.js +41 -4
- package/dist/attest.js.map +1 -1
- package/dist/errors.d.ts +2 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/snapshot.js +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -2
- package/dist/__tests__/fixtures.test.d.ts +0 -2
- package/dist/__tests__/fixtures.test.d.ts.map +0 -1
- package/dist/__tests__/fixtures.test.js +0 -37
- package/dist/__tests__/fixtures.test.js.map +0 -1
- package/dist/__tests__/v020.test.d.ts +0 -2
- package/dist/__tests__/v020.test.d.ts.map +0 -1
- package/dist/__tests__/v020.test.js +0 -408
- package/dist/__tests__/v020.test.js.map +0 -1
- package/dist/__tests__/vectors.test.d.ts +0 -2
- package/dist/__tests__/vectors.test.d.ts.map +0 -1
- package/dist/__tests__/vectors.test.js +0 -261
- package/dist/__tests__/vectors.test.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
# @nexart/ai-execution v0.
|
|
1
|
+
# @nexart/ai-execution v0.3.0
|
|
2
2
|
|
|
3
3
|
Tamper-evident records and Certified Execution Records (CER) for AI operations.
|
|
4
4
|
|
|
5
|
+
## Why Not Just Store Logs?
|
|
6
|
+
|
|
7
|
+
Logs tell you what happened. CERs prove it. A log entry can be edited, truncated, or fabricated after the fact with no way to detect it. A CER bundle is cryptographically sealed: any modification — to the input, output, parameters, or ordering — invalidates the certificate hash. If you need to demonstrate to an auditor, regulator, or downstream system that a specific AI model produced a specific output for a specific input with specific parameters, logs are insufficient. CERs provide the tamper-evident chain of custody that logs cannot.
|
|
8
|
+
|
|
5
9
|
## What This Does
|
|
6
10
|
|
|
7
11
|
This package creates integrity records for AI executions. Every time you call an AI model, it captures:
|
|
@@ -11,16 +15,16 @@ This package creates integrity records for AI executions. Every time you call an
|
|
|
11
15
|
- The exact parameters used (temperature, model, etc.)
|
|
12
16
|
- SHA-256 hashes of everything for tamper detection
|
|
13
17
|
|
|
14
|
-
These records can be verified
|
|
18
|
+
These records can be verified offline to prove the execution happened as recorded.
|
|
15
19
|
|
|
16
20
|
**Important:** This does NOT promise that an AI model will produce the same output twice. LLMs are not deterministic. This package provides **integrity and auditability** — proof that a specific input produced a specific output at a specific time with specific parameters.
|
|
17
21
|
|
|
18
22
|
## Compatibility Guarantees
|
|
19
23
|
|
|
20
|
-
- **v0.1.0 bundles verify forever.** Any CER bundle produced by
|
|
24
|
+
- **v0.1.0 and v0.2.0 bundles verify forever.** Any CER bundle produced by any prior version will pass `verify()` in v0.3.0 and all future versions.
|
|
21
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.
|
|
22
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.
|
|
23
|
-
- **If stricter canonicalization is
|
|
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.
|
|
24
28
|
|
|
25
29
|
## Installation
|
|
26
30
|
|
|
@@ -46,30 +50,24 @@ const cer = certifyDecision({
|
|
|
46
50
|
console.log(cer.certificateHash); // "sha256:..."
|
|
47
51
|
```
|
|
48
52
|
|
|
49
|
-
### Manual Snapshot + Seal
|
|
53
|
+
### Manual Snapshot + Seal
|
|
50
54
|
|
|
51
55
|
```typescript
|
|
52
|
-
import { createSnapshot,
|
|
56
|
+
import { createSnapshot, sealCer, verify } from '@nexart/ai-execution';
|
|
53
57
|
|
|
54
58
|
const snapshot = createSnapshot({
|
|
55
59
|
executionId: 'exec-001',
|
|
56
60
|
provider: 'openai',
|
|
57
61
|
model: 'gpt-4o',
|
|
58
|
-
modelVersion: '2026-01-01',
|
|
59
62
|
prompt: 'You are a helpful assistant.',
|
|
60
63
|
input: 'What is 2+2?',
|
|
61
64
|
parameters: { temperature: 0.7, maxTokens: 1024, topP: null, seed: null },
|
|
62
65
|
output: 'The answer is 4.',
|
|
63
66
|
});
|
|
64
67
|
|
|
65
|
-
const result = verifySnapshot(snapshot);
|
|
66
|
-
console.log(result.ok); // true
|
|
67
|
-
|
|
68
68
|
const bundle = sealCer(snapshot);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const cerResult = verifyCer(bundle);
|
|
72
|
-
console.log(cerResult.ok); // true
|
|
69
|
+
const result = verify(bundle);
|
|
70
|
+
console.log(result.ok); // true
|
|
73
71
|
```
|
|
74
72
|
|
|
75
73
|
### Agentic Multi-Step Workflow
|
|
@@ -79,28 +77,24 @@ import { RunBuilder } from '@nexart/ai-execution';
|
|
|
79
77
|
|
|
80
78
|
const run = new RunBuilder({ runId: 'analysis-run', workflowId: 'data-pipeline' });
|
|
81
79
|
|
|
82
|
-
|
|
83
|
-
provider: 'openai',
|
|
84
|
-
model: 'gpt-4o',
|
|
80
|
+
run.step({
|
|
81
|
+
provider: 'openai', model: 'gpt-4o',
|
|
85
82
|
prompt: 'Plan the analysis.',
|
|
86
83
|
input: 'Analyze Q1 sales data.',
|
|
87
84
|
output: 'I will: 1) load data, 2) compute totals, 3) summarize.',
|
|
88
85
|
parameters: { temperature: 0.3, maxTokens: 512, topP: null, seed: null },
|
|
89
86
|
});
|
|
90
|
-
// step0.snapshot.stepIndex === 0, prevStepHash === null
|
|
91
87
|
|
|
92
|
-
|
|
93
|
-
provider: 'openai',
|
|
94
|
-
model: 'gpt-4o',
|
|
88
|
+
run.step({
|
|
89
|
+
provider: 'openai', model: 'gpt-4o',
|
|
95
90
|
prompt: 'Execute step 1.',
|
|
96
91
|
input: 'Load and total Q1 data.',
|
|
97
92
|
output: 'Total revenue: $1.2M.',
|
|
98
93
|
parameters: { temperature: 0.3, maxTokens: 512, topP: null, seed: null },
|
|
99
94
|
});
|
|
100
|
-
// step1.snapshot.stepIndex === 1, prevStepHash === step0.certificateHash
|
|
101
95
|
|
|
102
96
|
const summary = run.finalize();
|
|
103
|
-
// { runId, stepCount: 2, steps: [...], finalStepHash:
|
|
97
|
+
// { runId, stepCount: 2, steps: [...], finalStepHash: "sha256:..." }
|
|
104
98
|
```
|
|
105
99
|
|
|
106
100
|
### Attest to Canonical Node
|
|
@@ -113,110 +107,20 @@ const proof = await attest(cer, {
|
|
|
113
107
|
nodeUrl: 'https://node.nexart.io',
|
|
114
108
|
apiKey: process.env.NEXART_API_KEY!,
|
|
115
109
|
});
|
|
116
|
-
console.log(proof.attestationId);
|
|
110
|
+
console.log(proof.attestationId);
|
|
117
111
|
```
|
|
118
112
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
import { exportCer, importCer, sealCer, createSnapshot } from '@nexart/ai-execution';
|
|
123
|
-
|
|
124
|
-
const bundle = sealCer(createSnapshot({ /* ... */ }));
|
|
125
|
-
|
|
126
|
-
// Export as canonical JSON string (deterministic, archive-safe)
|
|
127
|
-
const json = exportCer(bundle);
|
|
128
|
-
await fs.writeFile('audit/cer-001.json', json);
|
|
129
|
-
|
|
130
|
-
// Import and verify in one step (throws CerVerificationError if invalid)
|
|
131
|
-
const restored = importCer(await fs.readFile('audit/cer-001.json', 'utf-8'));
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### With JSON Input/Output
|
|
135
|
-
|
|
136
|
-
```typescript
|
|
137
|
-
const snapshot = createSnapshot({
|
|
138
|
-
executionId: 'exec-002',
|
|
139
|
-
provider: 'openai',
|
|
140
|
-
model: 'gpt-4o',
|
|
141
|
-
prompt: 'Extract entities',
|
|
142
|
-
input: { text: 'John lives in Paris', lang: 'en' },
|
|
143
|
-
parameters: { temperature: 0, maxTokens: 512, topP: null, seed: 42 },
|
|
144
|
-
output: { entities: ['John', 'Paris'], count: 2 },
|
|
145
|
-
});
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### OpenAI Provider (Optional)
|
|
149
|
-
|
|
150
|
-
Convenience adapter only; the core value of this package is the snapshot + hashing format.
|
|
151
|
-
The OpenAI adapter wraps a single `chat/completions` call and returns a sealed CER bundle.
|
|
152
|
-
**Provider determinism is not guaranteed** — identical inputs may produce different outputs across calls.
|
|
153
|
-
|
|
154
|
-
```typescript
|
|
155
|
-
import { runOpenAIChatExecution } from '@nexart/ai-execution/providers/openai';
|
|
156
|
-
|
|
157
|
-
const result = await runOpenAIChatExecution({
|
|
158
|
-
prompt: 'You are a helpful assistant.',
|
|
159
|
-
input: 'What is the capital of France?',
|
|
160
|
-
model: 'gpt-4o',
|
|
161
|
-
parameters: { temperature: 0.7, maxTokens: 256 },
|
|
162
|
-
});
|
|
113
|
+
Attestation verifies internal integrity only. It does not re-run the model. The node confirms the bundle's hashes are consistent and records it in the proof ledger.
|
|
163
114
|
|
|
164
|
-
|
|
165
|
-
console.log(result.snapshot.inputHash); // "sha256:..."
|
|
166
|
-
console.log(result.bundle.certificateHash); // "sha256:..."
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
Requires `OPENAI_API_KEY` environment variable or `apiKey` parameter.
|
|
170
|
-
|
|
171
|
-
### Anthropic Provider (Optional)
|
|
172
|
-
|
|
173
|
-
```typescript
|
|
174
|
-
import { runAnthropicExecution } from '@nexart/ai-execution/providers/anthropic';
|
|
175
|
-
|
|
176
|
-
const result = await runAnthropicExecution({
|
|
177
|
-
prompt: 'You are a helpful assistant.',
|
|
178
|
-
input: 'Explain quantum computing briefly.',
|
|
179
|
-
model: 'claude-sonnet-4-20250514',
|
|
180
|
-
parameters: { temperature: 0.5, maxTokens: 512 },
|
|
181
|
-
});
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
Requires `ANTHROPIC_API_KEY` environment variable or `apiKey` parameter.
|
|
185
|
-
|
|
186
|
-
### Generic Provider Wrapper
|
|
115
|
+
### Archive (Export / Import)
|
|
187
116
|
|
|
188
117
|
```typescript
|
|
189
|
-
import {
|
|
118
|
+
import { exportCer, importCer } from '@nexart/ai-execution';
|
|
190
119
|
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
callFn: async (input) => await myLlmClient.chat(input),
|
|
194
|
-
extractOutput: (raw) => raw.message,
|
|
195
|
-
extractModelVersion: (raw) => raw.modelVersion,
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
const result = await myProvider.execute({
|
|
199
|
-
providerInput: { messages: [{ role: 'user', content: 'Hello' }] },
|
|
200
|
-
prompt: 'System prompt',
|
|
201
|
-
input: 'Hello',
|
|
202
|
-
model: 'my-model-v2',
|
|
203
|
-
parameters: { temperature: 0.7, maxTokens: 1024, topP: null, seed: null },
|
|
204
|
-
});
|
|
120
|
+
const json = exportCer(bundle); // canonical JSON string
|
|
121
|
+
const restored = importCer(json); // parse + verify (throws on tamper)
|
|
205
122
|
```
|
|
206
123
|
|
|
207
|
-
## v0.2.0 Additions
|
|
208
|
-
|
|
209
|
-
| Feature | Description |
|
|
210
|
-
|---|---|
|
|
211
|
-
| `certifyDecision()` | One-call convenience: createSnapshot + sealCer combined |
|
|
212
|
-
| `RunBuilder` | Agentic workflow support with step chaining and prevStepHash |
|
|
213
|
-
| `attest()` | Post CER bundle to canonical node for ledger attestation |
|
|
214
|
-
| `exportCer()` / `importCer()` | Archive-safe canonical JSON serialization with verification |
|
|
215
|
-
| `wrapProvider()` | Generic provider wrapper factory |
|
|
216
|
-
| Anthropic adapter | `@nexart/ai-execution/providers/anthropic` |
|
|
217
|
-
| Typed errors | `CerVerificationError`, `CerAttestationError` |
|
|
218
|
-
| Workflow fields | `runId`, `stepId`, `stepIndex`, `workflowId`, `conversationId`, `prevStepHash` |
|
|
219
|
-
|
|
220
124
|
## Snapshot Format (ai.execution.v1)
|
|
221
125
|
|
|
222
126
|
### Required vs Optional Fields
|
|
@@ -235,24 +139,16 @@ const result = await myProvider.execute({
|
|
|
235
139
|
| `modelVersion` | Optional | `string \| null` | Defaults to `null` |
|
|
236
140
|
| `parameters.topP` | Optional | `number \| null` | Defaults to `null` |
|
|
237
141
|
| `parameters.seed` | Optional | `number \| null` | Defaults to `null` |
|
|
238
|
-
| `sdkVersion` | Optional | `string \| null` | Defaults to
|
|
142
|
+
| `sdkVersion` | Optional | `string \| null` | Defaults to `"0.3.0"` |
|
|
239
143
|
| `appId` | Optional | `string \| null` | Defaults to `null` |
|
|
240
|
-
| `runId` | Optional | `string \| null` | Workflow run ID
|
|
241
|
-
| `stepId` | Optional | `string \| null` | Step identifier within a run
|
|
242
|
-
| `stepIndex` | Optional | `number \| null` | 0-based step position
|
|
243
|
-
| `workflowId` | Optional | `string \| null` | Workflow template ID
|
|
244
|
-
| `conversationId` | Optional | `string \| null` | Conversation/session ID
|
|
245
|
-
| `prevStepHash` | Optional | `string \| null` | certificateHash of previous step
|
|
246
|
-
|
|
247
|
-
The following fields are **auto-generated** by `createSnapshot` and must not be set manually:
|
|
144
|
+
| `runId` | Optional | `string \| null` | Workflow run ID |
|
|
145
|
+
| `stepId` | Optional | `string \| null` | Step identifier within a run |
|
|
146
|
+
| `stepIndex` | Optional | `number \| null` | 0-based step position |
|
|
147
|
+
| `workflowId` | Optional | `string \| null` | Workflow template ID |
|
|
148
|
+
| `conversationId` | Optional | `string \| null` | Conversation/session ID |
|
|
149
|
+
| `prevStepHash` | Optional | `string \| null` | certificateHash of previous step |
|
|
248
150
|
|
|
249
|
-
|
|
250
|
-
|---|---|
|
|
251
|
-
| `type` | `"ai.execution.v1"` |
|
|
252
|
-
| `protocolVersion` | `"1.2.0"` |
|
|
253
|
-
| `executionSurface` | `"ai"` |
|
|
254
|
-
| `inputHash` | SHA-256 of input |
|
|
255
|
-
| `outputHash` | SHA-256 of output |
|
|
151
|
+
Auto-generated fields (set by `createSnapshot`, do not set manually): `type`, `protocolVersion`, `executionSurface`, `inputHash`, `outputHash`.
|
|
256
152
|
|
|
257
153
|
## CER Bundle Format
|
|
258
154
|
|
|
@@ -263,249 +159,109 @@ The following fields are **auto-generated** by `createSnapshot` and must not be
|
|
|
263
159
|
"createdAt": "2026-02-12T00:00:00.000Z",
|
|
264
160
|
"version": "0.1",
|
|
265
161
|
"snapshot": { ... },
|
|
266
|
-
"meta": {
|
|
267
|
-
"source": "my-app",
|
|
268
|
-
"tags": ["production"]
|
|
269
|
-
}
|
|
162
|
+
"meta": { "source": "my-app", "tags": ["production"] }
|
|
270
163
|
}
|
|
271
164
|
```
|
|
272
165
|
|
|
273
166
|
### Certificate Hash Computation
|
|
274
167
|
|
|
275
|
-
The `certificateHash` is SHA-256 of the UTF-8 bytes of the canonical JSON of
|
|
276
|
-
|
|
277
|
-
```
|
|
278
|
-
{ bundleType, version, createdAt, snapshot }
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
- `meta` is **excluded** from the certificate hash. It is an informational envelope field that does not affect integrity verification.
|
|
282
|
-
- Key-ordering rules apply **recursively** — every nested object within `snapshot` is also sorted lexicographically by key.
|
|
283
|
-
- The bytes being hashed are the UTF-8 encoding of the canonical JSON string (no BOM, no trailing newline).
|
|
284
|
-
- **This computation is identical in v0.1.0 and v0.2.0.** New optional snapshot fields (runId, stepIndex, etc.) participate in the hash only when present in the snapshot object.
|
|
285
|
-
|
|
286
|
-
## Hashing Rules
|
|
287
|
-
|
|
288
|
-
- **String values**: SHA-256 of UTF-8 bytes of the raw string
|
|
289
|
-
- **Object/Array values**: SHA-256 of UTF-8 bytes of canonical JSON (sorted keys, no whitespace, stable array order)
|
|
290
|
-
- All hashes use the format `sha256:<hex>` (lowercase hex, 64 characters)
|
|
291
|
-
|
|
292
|
-
## Canonical JSON Constraints
|
|
293
|
-
|
|
294
|
-
Canonical JSON is a deterministic subset of JSON. The following rules apply:
|
|
295
|
-
|
|
296
|
-
1. **Object keys** are sorted lexicographically (Unicode codepoint order) at every nesting level.
|
|
297
|
-
2. **No whitespace** — no spaces, tabs, or newlines between tokens.
|
|
298
|
-
3. **Array order is preserved** — arrays are never re-sorted.
|
|
299
|
-
4. **`null`** is valid and serialized as `null`.
|
|
300
|
-
5. **Numbers** must be finite. `NaN`, `Infinity`, and `-Infinity` are **rejected** (throw).
|
|
301
|
-
6. **`undefined`** values in object properties are **omitted** (the key is dropped).
|
|
302
|
-
7. **`BigInt`**, **functions**, and **`Symbol`** are **not valid** JSON types and are **rejected** (throw).
|
|
303
|
-
8. Strings are JSON-escaped (e.g. `"` → `\"`).
|
|
304
|
-
|
|
305
|
-
These constraints ensure that the same logical value always produces the same byte sequence, which is essential for hash stability across implementations and languages.
|
|
306
|
-
|
|
307
|
-
**Note on number formatting:** v1 bundles use `JSON.stringify()` for number-to-string conversion, which is consistent across JavaScript engines but does not implement RFC 8785 (JCS) number formatting for edge cases like `-0`. If stricter canonicalization is required in the future, it will be introduced via a new bundle type (`cer.ai.execution.v2`), not by modifying v1 behavior.
|
|
308
|
-
|
|
309
|
-
## RunBuilder: Agentic Workflow Chaining
|
|
310
|
-
|
|
311
|
-
The `RunBuilder` class enables multi-step AI workflows with cryptographic chaining:
|
|
312
|
-
|
|
313
|
-
- **Auto-incrementing `stepIndex`**: starts at 0, increments with each `.step()` call.
|
|
314
|
-
- **`prevStepHash` chaining**: each step's `prevStepHash` is set to the `certificateHash` of the previous step (null for step 0).
|
|
315
|
-
- **`executionId` generation**: automatically set to `{runId}-step-{stepIndex}`.
|
|
316
|
-
- Each step produces a full, independently verifiable CER bundle.
|
|
317
|
-
- Call `.finalize()` to get a `RunSummary` with all step hashes and chain metadata.
|
|
168
|
+
The `certificateHash` is SHA-256 of the UTF-8 bytes of the canonical JSON of exactly: `{ bundleType, version, createdAt, snapshot }`. `meta` is excluded. Key-ordering is recursive. This computation is identical across all SDK versions.
|
|
318
169
|
|
|
319
170
|
## Attestation
|
|
320
171
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
```typescript
|
|
324
|
-
const proof = await attest(bundle, {
|
|
325
|
-
nodeUrl: 'https://node.nexart.io',
|
|
326
|
-
apiKey: 'your-api-key',
|
|
327
|
-
});
|
|
328
|
-
```
|
|
172
|
+
Endpoint: `POST {nodeUrl}/api/attest`
|
|
329
173
|
|
|
330
|
-
- Endpoint: `POST {nodeUrl}/api/attest`
|
|
331
174
|
- Authorization: `Bearer {apiKey}`
|
|
332
175
|
- Body: the full CER bundle as JSON
|
|
333
176
|
- Returns: `AttestationResult` with `attestationId`, `nodeRuntimeHash`, `certificateHash`, `protocolVersion`
|
|
334
|
-
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
| Error | When thrown |
|
|
339
|
-
|---|---|
|
|
340
|
-
| `CerVerificationError` | `importCer()` receives invalid or tampered JSON |
|
|
341
|
-
| `CerAttestationError` | `attest()` fails (network error, HTTP error, invalid response) |
|
|
342
|
-
|
|
343
|
-
Both extend `Error` and include structured data:
|
|
344
|
-
- `CerVerificationError.errors`: `string[]` of specific verification failures
|
|
345
|
-
- `CerAttestationError.statusCode`: HTTP status code (if available)
|
|
346
|
-
- `CerAttestationError.responseBody`: raw response body (if available)
|
|
347
|
-
|
|
348
|
-
## Interoperability (Test Vectors)
|
|
349
|
-
|
|
350
|
-
Cross-language implementations **must** match the test vectors exactly. Vectors are committed as JSON fixtures at:
|
|
351
|
-
|
|
352
|
-
```
|
|
353
|
-
packages/ai-execution/fixtures/vectors/
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
### Vector 001 (single decision)
|
|
357
|
-
|
|
358
|
-
See `vector-001.snapshot.json` and `vector-001.expected.json` for a single text-in/text-out CER with committed hash values.
|
|
359
|
-
|
|
360
|
-
### Vector 002 (3-step agentic chain)
|
|
361
|
-
|
|
362
|
-
See `vector-002.chain.json` for a 3-step workflow chain with:
|
|
363
|
-
- Step 0: `prevStepHash: null`, `stepIndex: 0`
|
|
364
|
-
- Step 1: `prevStepHash: step0.certificateHash`, `stepIndex: 1`
|
|
365
|
-
- Step 2: `prevStepHash: step1.certificateHash`, `stepIndex: 2`
|
|
366
|
-
|
|
367
|
-
Each entry includes `snapshot`, `expectedCertificateHash`, and `cerCreatedAt` for deterministic verification.
|
|
368
|
-
|
|
369
|
-
### Golden Fixtures (backward compatibility)
|
|
370
|
-
|
|
371
|
-
5 CER bundles produced with v0.1.0 semantics are committed at:
|
|
372
|
-
|
|
373
|
-
```
|
|
374
|
-
packages/ai-execution/fixtures/golden/
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
These bundles must verify with every future version of the library.
|
|
378
|
-
|
|
379
|
-
## Threat Model
|
|
380
|
-
|
|
381
|
-
This package provides **integrity and auditability** for AI execution records.
|
|
177
|
+
- Default timeout: 10 seconds (configurable via `timeoutMs`)
|
|
178
|
+
- Validates: response `certificateHash` matches submitted bundle; all hashes in `sha256:<64hex>` format
|
|
179
|
+
- Throws: `CerAttestationError` on mismatch, network error, timeout, or HTTP error
|
|
382
180
|
|
|
383
|
-
|
|
181
|
+
Attestation verifies internal integrity only. It does not re-run the model or validate the correctness of the AI output.
|
|
384
182
|
|
|
385
|
-
|
|
386
|
-
- **Parameter laundering** — changing temperature, model, seed, or other parameters after the fact is detectable.
|
|
387
|
-
- **Silent record edits** — any modification to a sealed CER bundle is detectable via `verifyCer()`.
|
|
388
|
-
- **Chain forgery** — in multi-step workflows, `prevStepHash` links each step to the previous, making it impossible to insert, remove, or reorder steps without detection.
|
|
389
|
-
|
|
390
|
-
### What it does NOT prevent
|
|
391
|
-
|
|
392
|
-
- **Provider lying** — if the AI provider returns fabricated output, this package faithfully records it.
|
|
393
|
-
- **Model drift** — models change over time. `modelVersion` helps track this but cannot prevent it.
|
|
394
|
-
- **Correctness of the answer** — this package records what was said, not whether it was right.
|
|
395
|
-
|
|
396
|
-
## Verification Best Practice
|
|
397
|
-
|
|
398
|
-
**Recommended flow:**
|
|
399
|
-
|
|
400
|
-
1. Call `certifyDecision(...)` (or `createSnapshot` + `sealCer`).
|
|
401
|
-
2. Call `verifyCer(bundle)` **before** storing — catches any in-memory corruption.
|
|
402
|
-
3. Store the full bundle as an immutable audit artifact.
|
|
403
|
-
4. Optionally call `attest(bundle, ...)` to register on the canonical node ledger.
|
|
404
|
-
5. On retrieval, call `verifyCer(bundle)` again to confirm integrity.
|
|
405
|
-
|
|
406
|
-
```typescript
|
|
407
|
-
const bundle = certifyDecision({ ... });
|
|
408
|
-
|
|
409
|
-
const check = verifyCer(bundle);
|
|
410
|
-
if (!check.ok) {
|
|
411
|
-
throw new Error(`CER integrity failure: ${check.errors.join('; ')}`);
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
await store(bundle);
|
|
183
|
+
## Canonical JSON Constraints
|
|
415
184
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
185
|
+
1. Object keys sorted lexicographically (Unicode codepoint order) at every nesting level.
|
|
186
|
+
2. No whitespace between tokens.
|
|
187
|
+
3. Array order preserved.
|
|
188
|
+
4. `null` serialized as `null`.
|
|
189
|
+
5. Numbers must be finite. `NaN`, `Infinity`, `-Infinity` rejected (throw).
|
|
190
|
+
6. `undefined` values in object properties omitted (key dropped).
|
|
191
|
+
7. `BigInt`, functions, `Symbol` rejected (throw).
|
|
192
|
+
8. Strings JSON-escaped.
|
|
419
193
|
|
|
420
|
-
|
|
194
|
+
**Canonicalization is frozen for v1.** Number formatting uses `JSON.stringify()` (V8-consistent). This does not normalize `-0` to `0` and does not implement RFC 8785 exponential notation rules. These are documented known behaviors, not bugs. Any future stricter canonicalization will ship under a new bundle type.
|
|
421
195
|
|
|
422
|
-
|
|
196
|
+
## Error Types
|
|
423
197
|
|
|
424
|
-
|
|
425
|
-
|
|
198
|
+
| Error | When thrown | Structured data |
|
|
199
|
+
|---|---|---|
|
|
200
|
+
| `CerVerificationError` | `importCer()` on invalid/tampered data | `.errors: string[]` |
|
|
201
|
+
| `CerAttestationError` | `attest()` on failure | `.statusCode`, `.responseBody`, `.details: string[]` |
|
|
426
202
|
|
|
427
|
-
|
|
203
|
+
## Interoperability (Test Vectors)
|
|
428
204
|
|
|
429
|
-
-
|
|
430
|
-
- A dedicated redacted bundle type (`cer.ai.execution.redacted.v1`) is planned for a future release.
|
|
431
|
-
- Treat CER bundles with the same access controls as the underlying data they contain.
|
|
205
|
+
Fixtures at `fixtures/vectors/` and `fixtures/golden/`. Cross-language implementations must match committed hash values exactly. Golden fixtures (v0.1.0 semantics) must verify with every future version.
|
|
432
206
|
|
|
433
207
|
## API Reference
|
|
434
208
|
|
|
435
209
|
### Core
|
|
436
210
|
|
|
437
211
|
| Function | Description |
|
|
438
|
-
|
|
439
|
-
| `createSnapshot(params)` | Create
|
|
212
|
+
|---|---|
|
|
213
|
+
| `createSnapshot(params)` | Create snapshot with computed hashes |
|
|
440
214
|
| `verifySnapshot(snapshot)` | Verify snapshot hashes and structure |
|
|
441
|
-
| `sealCer(snapshot, options?)` | Seal
|
|
442
|
-
| `verifyCer(bundle)` | Verify
|
|
443
|
-
| `certifyDecision(params)` | One-call: createSnapshot + sealCer
|
|
215
|
+
| `sealCer(snapshot, options?)` | Seal snapshot into CER bundle |
|
|
216
|
+
| `verify(bundle)` / `verifyCer(bundle)` | Verify CER bundle |
|
|
217
|
+
| `certifyDecision(params)` | One-call: createSnapshot + sealCer |
|
|
444
218
|
|
|
445
219
|
### Workflow
|
|
446
220
|
|
|
447
|
-
|
|
|
448
|
-
|
|
449
|
-
| `RunBuilder` | Multi-step workflow builder with
|
|
450
|
-
| `RunBuilder.step(params)` | Add a step, returns sealed CER bundle |
|
|
451
|
-
| `RunBuilder.finalize()` | Returns RunSummary with all step hashes |
|
|
452
|
-
|
|
453
|
-
### Attestation
|
|
454
|
-
|
|
455
|
-
| Function | Description |
|
|
456
|
-
|----------|-------------|
|
|
457
|
-
| `attest(bundle, options)` | Post CER bundle to canonical node **(v0.2.0)** |
|
|
458
|
-
|
|
459
|
-
### Archive
|
|
460
|
-
|
|
461
|
-
| Function | Description |
|
|
462
|
-
|----------|-------------|
|
|
463
|
-
| `exportCer(bundle)` | Serialize bundle to canonical JSON string **(v0.2.0)** |
|
|
464
|
-
| `importCer(json)` | Parse + verify bundle from JSON string **(v0.2.0)** |
|
|
465
|
-
|
|
466
|
-
### Hashing
|
|
467
|
-
|
|
468
|
-
| Function | Description |
|
|
469
|
-
|----------|-------------|
|
|
470
|
-
| `computeInputHash(input)` | Compute hash for input (string or object) |
|
|
471
|
-
| `computeOutputHash(output)` | Compute hash for output (string or object) |
|
|
472
|
-
| `toCanonicalJson(value)` | Deterministic JSON serialization |
|
|
473
|
-
| `sha256Hex(data)` | Raw SHA-256 hex digest |
|
|
474
|
-
| `hashUtf8(value)` | Hash a UTF-8 string |
|
|
475
|
-
| `hashCanonicalJson(value)` | Hash canonical JSON of a value |
|
|
221
|
+
| Export | Description |
|
|
222
|
+
|---|---|
|
|
223
|
+
| `RunBuilder` | Multi-step workflow builder with prevStepHash chaining |
|
|
476
224
|
|
|
477
|
-
###
|
|
225
|
+
### Attestation & Archive
|
|
478
226
|
|
|
479
227
|
| Function | Description |
|
|
480
|
-
|
|
481
|
-
| `
|
|
482
|
-
| `
|
|
483
|
-
| `
|
|
228
|
+
|---|---|
|
|
229
|
+
| `attest(bundle, options)` | Post CER to canonical node |
|
|
230
|
+
| `exportCer(bundle)` | Serialize to canonical JSON string |
|
|
231
|
+
| `importCer(json)` | Parse + verify from JSON string |
|
|
484
232
|
|
|
485
|
-
###
|
|
233
|
+
### Providers (sub-exports)
|
|
486
234
|
|
|
487
|
-
|
|
|
488
|
-
|
|
489
|
-
| `
|
|
490
|
-
| `
|
|
235
|
+
| Function | Export path |
|
|
236
|
+
|---|---|
|
|
237
|
+
| `runOpenAIChatExecution` | `@nexart/ai-execution/providers/openai` |
|
|
238
|
+
| `runAnthropicExecution` | `@nexart/ai-execution/providers/anthropic` |
|
|
239
|
+
| `wrapProvider` | `@nexart/ai-execution/providers/wrap` |
|
|
491
240
|
|
|
492
|
-
## Version
|
|
241
|
+
## Version History
|
|
493
242
|
|
|
494
|
-
| Version |
|
|
495
|
-
|
|
496
|
-
| v0.1.0 |
|
|
497
|
-
|
|
|
498
|
-
|
|
|
243
|
+
| Version | Description |
|
|
244
|
+
|---|---|
|
|
245
|
+
| v0.1.0 | Core snapshot + CER + verify + OpenAI adapter |
|
|
246
|
+
| v0.2.0 | certifyDecision, RunBuilder, attest, archive, Anthropic, wrapProvider, typed errors, workflow fields |
|
|
247
|
+
| **v0.3.0** | Attestation hardening (hash validation, timeout), `verify` alias, `CerAttestationError.details`, release hygiene |
|
|
248
|
+
| v1.0.0 | Planned: API stabilization, freeze public API surface |
|
|
499
249
|
|
|
500
|
-
##
|
|
250
|
+
## Releasing
|
|
501
251
|
|
|
502
252
|
```bash
|
|
503
253
|
cd packages/ai-execution
|
|
504
254
|
npm run build
|
|
505
|
-
npm
|
|
255
|
+
npm test
|
|
506
256
|
npm publish --access public
|
|
507
257
|
```
|
|
508
258
|
|
|
259
|
+
The `release` script automates build, test, version bump, and publish:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
npm run release
|
|
263
|
+
```
|
|
264
|
+
|
|
509
265
|
## License
|
|
510
266
|
|
|
511
267
|
MIT
|
package/dist/attest.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attest.d.ts","sourceRoot":"","sources":["../src/attest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"attest.d.ts","sourceRoot":"","sources":["../src/attest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAczF,wBAAsB,MAAM,CAC1B,MAAM,EAAE,oBAAoB,EAC5B,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,iBAAiB,CAAC,CA0F5B"}
|
package/dist/attest.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { CerAttestationError } from './errors.js';
|
|
2
|
+
const SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
3
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
4
|
+
function validateHashFormat(value, fieldName) {
|
|
5
|
+
if (typeof value !== 'string')
|
|
6
|
+
return null;
|
|
7
|
+
if (!SHA256_PATTERN.test(value)) {
|
|
8
|
+
return `${fieldName} is not in sha256:<64hex> format: "${value}"`;
|
|
9
|
+
}
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
2
12
|
export async function attest(bundle, options) {
|
|
3
13
|
const url = `${options.nodeUrl.replace(/\/+$/, '')}/api/attest`;
|
|
14
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
15
|
+
const controller = new AbortController();
|
|
16
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
4
17
|
let response;
|
|
5
18
|
try {
|
|
6
19
|
response = await fetch(url, {
|
|
@@ -10,10 +23,19 @@ export async function attest(bundle, options) {
|
|
|
10
23
|
'Authorization': `Bearer ${options.apiKey}`,
|
|
11
24
|
},
|
|
12
25
|
body: JSON.stringify(bundle),
|
|
26
|
+
signal: controller.signal,
|
|
13
27
|
});
|
|
14
28
|
}
|
|
15
29
|
catch (err) {
|
|
16
|
-
|
|
30
|
+
clearTimeout(timer);
|
|
31
|
+
const error = err;
|
|
32
|
+
if (error.name === 'AbortError') {
|
|
33
|
+
throw new CerAttestationError(`Attestation request timed out after ${timeoutMs}ms`);
|
|
34
|
+
}
|
|
35
|
+
throw new CerAttestationError(`Network error contacting attestation node: ${error.message}`);
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
clearTimeout(timer);
|
|
17
39
|
}
|
|
18
40
|
let body;
|
|
19
41
|
try {
|
|
@@ -24,12 +46,27 @@ export async function attest(bundle, options) {
|
|
|
24
46
|
throw new CerAttestationError(`Attestation node returned non-JSON response (${response.status}): ${text}`, response.status);
|
|
25
47
|
}
|
|
26
48
|
if (!response.ok) {
|
|
27
|
-
const
|
|
28
|
-
|
|
49
|
+
const result = body;
|
|
50
|
+
const msg = typeof result.error === 'string'
|
|
51
|
+
? result.error
|
|
29
52
|
: `HTTP ${response.status}`;
|
|
30
|
-
|
|
53
|
+
const details = Array.isArray(result.details) ? result.details : undefined;
|
|
54
|
+
throw new CerAttestationError(`Attestation failed: ${msg}`, response.status, body, details);
|
|
31
55
|
}
|
|
32
56
|
const result = body;
|
|
57
|
+
const errors = [];
|
|
58
|
+
if (typeof result.certificateHash === 'string' && result.certificateHash !== bundle.certificateHash) {
|
|
59
|
+
errors.push(`Node returned certificateHash "${result.certificateHash}" but bundle has "${bundle.certificateHash}"`);
|
|
60
|
+
}
|
|
61
|
+
const certHashErr = validateHashFormat(result.certificateHash, 'response.certificateHash');
|
|
62
|
+
if (certHashErr)
|
|
63
|
+
errors.push(certHashErr);
|
|
64
|
+
const runtimeHashErr = validateHashFormat(result.nodeRuntimeHash, 'response.nodeRuntimeHash');
|
|
65
|
+
if (runtimeHashErr)
|
|
66
|
+
errors.push(runtimeHashErr);
|
|
67
|
+
if (errors.length > 0) {
|
|
68
|
+
throw new CerAttestationError(`Attestation response validation failed: ${errors.join('; ')}`, response.status, body, errors);
|
|
69
|
+
}
|
|
33
70
|
return {
|
|
34
71
|
ok: true,
|
|
35
72
|
attestationId: typeof result.attestationId === 'string' ? result.attestationId : undefined,
|
package/dist/attest.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attest.js","sourceRoot":"","sources":["../src/attest.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,MAA4B,EAC5B,OAAsB;IAEtB,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"attest.js","sourceRoot":"","sources":["../src/attest.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export declare class CerVerificationError extends Error {
|
|
|
5
5
|
export declare class CerAttestationError extends Error {
|
|
6
6
|
readonly statusCode?: number;
|
|
7
7
|
readonly responseBody?: unknown;
|
|
8
|
-
|
|
8
|
+
readonly details?: string[];
|
|
9
|
+
constructor(message: string, statusCode?: number, responseBody?: unknown, details?: string[]);
|
|
9
10
|
}
|
|
10
11
|
//# sourceMappingURL=errors.d.ts.map
|