verdifax 1.0.0 → 1.1.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 +60 -5
- package/dist/index.d.mts +219 -18
- package/dist/index.d.ts +219 -18
- package/dist/index.js +436 -92
- package/dist/index.mjs +430 -92
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,13 +80,17 @@ if (v.verified) {
|
|
|
80
80
|
| `client.getPublicBundleByToken(token)` | `GET /public/runs/{token}/bundle.json` | none |
|
|
81
81
|
| `client.dcaeVerify(bundle)` | `POST /dcae/verify` | none |
|
|
82
82
|
| `client.registerRequestKey()` | `POST /keys/request-key` | required |
|
|
83
|
+
| `client.rotateRequestKey(newSigningKey)` | `POST /keys/request-key` | required |
|
|
83
84
|
| `client.clearRequestKey()` | `DELETE /keys/request-key` | required |
|
|
84
85
|
| `client.adminCreateKey(name)` | `POST /admin/keys` | admin |
|
|
85
86
|
| `client.adminListKeys()` | `GET /admin/keys` | admin |
|
|
86
87
|
| `client.adminRevokeKey(id)` | `DELETE /admin/keys/{id}` | admin |
|
|
88
|
+
| `client.adminClearRequestKey(id)` | `DELETE /admin/keys/{id}/request-key` | admin |
|
|
87
89
|
| `client.adminErase(req)` | `POST /admin/erase` | admin |
|
|
88
90
|
|
|
89
|
-
|
|
91
|
+
`client.job(jobId)` returns a [job helper](#jobs) and makes no request until a step runs.
|
|
92
|
+
|
|
93
|
+
These functions make no request: `captureEnvironment`, `formatProviderPayload`, `signRequest`, `signRequestV2`, `requestKeyRegistrationBody`, `requestKeyChangeApproval`, `requestKeyChangePreimage`, `requestSignaturePreimage`, `requestSignaturePreimageV2`, `generateRequestKeyPair`, `validateHex64`, `validateJobName`, `validateRouteId`, `dseDispatchHash`, `dcaeClosureHash` and `cresReceiptHash`. `RUN_STATUSES` lists every run status the server uses.
|
|
90
94
|
|
|
91
95
|
`getRunArtifacts`, `getPublicBundle` and `getPublicBundleByToken` return the exact JSON text the server sent, so you can save it and check it with `verdifax-verify`.
|
|
92
96
|
|
|
@@ -126,6 +130,44 @@ for (const row of batch.results) {
|
|
|
126
130
|
|
|
127
131
|
The client's timeout covers the whole batch, so raise `timeoutMs` for a large one. A batch is one POST and it is never retried. After a timeout the server may have run some or all of the rows, so list your runs before you send them again.
|
|
128
132
|
|
|
133
|
+
## Jobs
|
|
134
|
+
|
|
135
|
+
A job links several runs into one map: each step names the records it directly follows, so work can split into branches and join again. `client.job(jobId)` returns a helper that keeps the map for you.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
const job = client.job("loan-1042");
|
|
139
|
+
const run = { program_id, route_id, registry_record_hash };
|
|
140
|
+
|
|
141
|
+
await job.step("intake", { ...run, payload_text: "application received" });
|
|
142
|
+
|
|
143
|
+
// Two branches after one step.
|
|
144
|
+
await job.step("credit", { ...run, payload_text: "credit check", after: ["intake"] });
|
|
145
|
+
await job.step("income", { ...run, payload_text: "income check", after: ["intake"] });
|
|
146
|
+
|
|
147
|
+
// A final step joining them.
|
|
148
|
+
await job.step("decide", {
|
|
149
|
+
...run,
|
|
150
|
+
payload_text: "decision",
|
|
151
|
+
after: ["credit", "income"],
|
|
152
|
+
final: true,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
console.log(job.recordHashes); // step id to record hash
|
|
156
|
+
console.log(job.signedRequests); // step id to signed text, when the client signs
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`job.step(stepId, options)` takes everything `attest` takes, plus `after`, `external_previous` and `final`, and returns what `attest` returns. `after` lists step ids in this job. Without it, a step follows the one run just before it through this helper, and the first step follows nothing. An `after` that names an unknown step throws a `VerdifaxError` before anything is sent.
|
|
160
|
+
|
|
161
|
+
A refused or halted step stays in the map, so a later step can follow it. `job.step` records its hash and signed text, then throws the same `APIError` that `attest` throws, with the hash in `err.recordHash`. A step that sealed nothing, such as a timeout or a 409, is not in the map and does not move the default `after`, so it can run again under the same id. To run branches at the same time, give each one its `after`. An empty `after` starts another root step.
|
|
162
|
+
|
|
163
|
+
A successful step records `record_hash`, or `manifest_hash` if the response has no `record_hash`. If it has neither, `job.step` throws a `VerdifaxError`, so the map never silently gets a gap. A mistyped option such as `afer` is refused before sending, so a typo cannot silently drop a link.
|
|
164
|
+
|
|
165
|
+
`execute`, `attest` and each row of `executeBatch` also take a `job` block directly: `{ job_id, step_id, previous_record_hashes, external_previous, final }`. Results carry `record_hash`. `job_id` and `step_id` are 1 to 128 letters, digits, `.`, `_`, `:` or `-`, and any other name is refused before sending. The SDK sorts both lists and removes repeats, and refuses unknown fields, a `final` that is not true or false, and hash lists that are not lists. A determinism check runs twice, so `verifyDeterminism` refuses a job.
|
|
166
|
+
|
|
167
|
+
The server refuses a step in a closed job, a repeated step id, and a final step that leaves a branch with no step after it. To check a whole job, save each record in one folder and run `verdifax-verify --job FOLDER`.
|
|
168
|
+
|
|
169
|
+
A job map covers what was sent. It cannot show a step that was never sent.
|
|
170
|
+
|
|
129
171
|
## Evidence export
|
|
130
172
|
|
|
131
173
|
`exportRuns` lists the runs the server holds for your key in a period, failures included, with up to 10,000 rows per export. `exportRunsCSV` returns the same rows as raw CSV text.
|
|
@@ -142,6 +184,8 @@ if (truncated) {
|
|
|
142
184
|
}
|
|
143
185
|
```
|
|
144
186
|
|
|
187
|
+
Pass `job_id` in the period to export one job. JSON rows also carry `anchor_source` (`bundle`, `receipt` or `none`), and `job_id` and `step_id` when the run is a job step.
|
|
188
|
+
|
|
145
189
|
The export states what the server stored, not a verdict. A call that never reached Verdifax cannot appear in it. Both forms say when a period holds more rows than the export returned: check `truncated`. `exportRunsCSV` reads `total` and `truncated` from the `X-Verdifax-Total` and `X-Verdifax-Truncated` response headers, and throws if the server does not send them. In the CSV, a `route_id` that a spreadsheet could read as a formula gets a leading `'`.
|
|
146
190
|
|
|
147
191
|
## Provider helpers
|
|
@@ -159,6 +203,8 @@ await client.attestClaudeResponse({
|
|
|
159
203
|
});
|
|
160
204
|
```
|
|
161
205
|
|
|
206
|
+
Both helpers take the same optional `job` block as `attest` and return `record_hash` and `signed_request` the same way.
|
|
207
|
+
|
|
162
208
|
`govern` is false by default, which changes earlier behavior. Set `govern: true` to also send the response text as `ai_output_text` for the AIVP review, which runs on Anthropic in production. Without it, the AI output text is not sent for review.
|
|
163
209
|
|
|
164
210
|
## Optional execute fields
|
|
@@ -219,6 +265,7 @@ Request signing is optional. A client with no signing key sends the same request
|
|
|
219
265
|
You can register an Ed25519 public key on your API key, so that each run must also be signed with a private key that only you hold. The key pair is created on your machine, and Verdifax receives only the public key.
|
|
220
266
|
|
|
221
267
|
```typescript
|
|
268
|
+
import { writeFileSync } from "node:fs";
|
|
222
269
|
import { VerdifaxClient, generateRequestKeyPair } from "verdifax";
|
|
223
270
|
|
|
224
271
|
// Run once, then store privateKey as a secret.
|
|
@@ -233,21 +280,29 @@ await client.registerRequestKey(); // sends the public key with its proof
|
|
|
233
280
|
|
|
234
281
|
// execute, attest, attestClaudeResponse, attestOpenAIResponse, executeBatch
|
|
235
282
|
// and verifyDeterminism now sign.
|
|
236
|
-
await client.execute({
|
|
283
|
+
const run = await client.execute({
|
|
237
284
|
program_id: "1111111111111111111111111111111111111111111111111111111111111111",
|
|
238
285
|
route_id: "demo-attestation",
|
|
239
286
|
registry_record_hash: "2222222222222222222222222222222222222222222222222222222222222222",
|
|
240
287
|
payload_text: "first signed attestation",
|
|
241
288
|
});
|
|
242
289
|
|
|
243
|
-
|
|
290
|
+
// The exact text that was signed. Keep it for verdifax-verify --request FILE.
|
|
291
|
+
writeFileSync("signed-request.json", run.signed_request!);
|
|
292
|
+
|
|
293
|
+
// The current key approves the change, and this client then signs with the new key.
|
|
294
|
+
await client.rotateRequestKey(generateRequestKeyPair().privateKey);
|
|
295
|
+
|
|
296
|
+
await client.clearRequestKey(); // stops requiring signatures, and this client stops signing
|
|
244
297
|
```
|
|
245
298
|
|
|
246
|
-
|
|
299
|
+
A signed call serializes the request once and signs that exact text, using signature version 2, so the signature covers the whole request. It sends `signed_request` and `request_signature` and nothing else, and a batch row has the same shape. The result carries the text as `signed_request`, on each row for a batch. A refused or halted run throws the usual `APIError`, with the text in `err.signedRequest`. It holds your payload, so it stays out of the error message and logging the error does not print it: read it by name.
|
|
300
|
+
|
|
301
|
+
Registering a key changes that API key for every client that uses it: the server then refuses a run without a valid signature, so every caller on the key needs the private key. An API key alone cannot replace or clear a registered key: `rotateRequestKey` and `clearRequestKey` send the current key's approval. After a successful `clearRequestKey` the client drops its signing key and sends unsigned requests, because the server refuses a signed run on an API key with no registered key. To sign again, create a client with a `signingKey` and register it. Each request carries the time and a fresh random nonce. The server refuses a repeated nonce and a time more than five minutes from its own clock.
|
|
247
302
|
|
|
248
303
|
In a batch each row carries its own signature and nonce, and the server checks them as it reaches that row. A row refused for its signature comes back in `results`, so a repeated batch returns HTTP 200 with failed rows, and a row reached more than five minutes after it was signed fails the same way. A determinism check uses one signature for the request, and repeating it returns HTTP 403.
|
|
249
304
|
|
|
250
|
-
|
|
305
|
+
A signature shows who requested the run, not that they accepted the outcome.
|
|
251
306
|
|
|
252
307
|
## Trust model
|
|
253
308
|
|
package/dist/index.d.mts
CHANGED
|
@@ -68,9 +68,27 @@ interface ExecuteRequest {
|
|
|
68
68
|
/** Caller-supplied record of what led to this run. Recorded as
|
|
69
69
|
* supplied. */
|
|
70
70
|
causal_graph?: Record<string, unknown>;
|
|
71
|
-
/**
|
|
71
|
+
/** Places this run in a job map. Not accepted by a determinism check,
|
|
72
|
+
* which runs twice. */
|
|
73
|
+
job?: JobLink;
|
|
74
|
+
/** A version 1 signature for an unsigned client to send. A client with
|
|
75
|
+
* the `signingKey` option sends version 2 and drops this field. */
|
|
72
76
|
request_signature?: RequestSignature;
|
|
73
77
|
}
|
|
78
|
+
/** Places one run in a job. `job_id` and `step_id` are 1 to 128 letters,
|
|
79
|
+
* digits, dots, underscores, colons or dashes. The client sorts both
|
|
80
|
+
* lists, removes repeats and leaves out an empty list and a false
|
|
81
|
+
* `final`. */
|
|
82
|
+
interface JobLink {
|
|
83
|
+
job_id: string;
|
|
84
|
+
step_id: string;
|
|
85
|
+
/** Record hashes of the steps this one directly follows, in this job. */
|
|
86
|
+
previous_record_hashes?: Hex64[];
|
|
87
|
+
/** Record hashes from another account that this step follows. */
|
|
88
|
+
external_previous?: Hex64[];
|
|
89
|
+
/** True on the step that closes the job. */
|
|
90
|
+
final?: boolean;
|
|
91
|
+
}
|
|
74
92
|
/** The runtime a caller declares for a run. Every field is optional.
|
|
75
93
|
* The server records these values as given and does not check them. */
|
|
76
94
|
interface ReproducibilityContext {
|
|
@@ -98,6 +116,27 @@ interface RequestSignature {
|
|
|
98
116
|
/** 16 random bytes as 32 lowercase hex characters, fresh per request. */
|
|
99
117
|
nonce: string;
|
|
100
118
|
}
|
|
119
|
+
/** A version 2 request signature, which covers the exact `signed_request`
|
|
120
|
+
* text. */
|
|
121
|
+
interface RequestSignatureV2 extends RequestSignature {
|
|
122
|
+
version: "2";
|
|
123
|
+
}
|
|
124
|
+
/** The whole body of a signed call, and of each row of a signed batch.
|
|
125
|
+
* The server reads the request from `signed_request` alone. */
|
|
126
|
+
interface SignedRequestBody {
|
|
127
|
+
/** The request as JSON text, exactly as it was signed. */
|
|
128
|
+
signed_request: string;
|
|
129
|
+
request_signature: RequestSignatureV2;
|
|
130
|
+
}
|
|
131
|
+
/** The current key's approval of replacing or clearing itself. */
|
|
132
|
+
interface RequestKeyChange {
|
|
133
|
+
/** Base64 Ed25519 signature by the current key. */
|
|
134
|
+
signature: string;
|
|
135
|
+
/** RFC 3339 UTC with whole seconds. */
|
|
136
|
+
signed_at: string;
|
|
137
|
+
/** 16 random bytes as 32 lowercase hex characters, fresh per change. */
|
|
138
|
+
nonce: string;
|
|
139
|
+
}
|
|
101
140
|
/** A key pair from {@link generateRequestKeyPair}, as raw bytes. */
|
|
102
141
|
interface RequestKeyPair {
|
|
103
142
|
/** The 32-byte Ed25519 public key. */
|
|
@@ -112,7 +151,8 @@ interface RequestKeyRegistrationBody {
|
|
|
112
151
|
/** Base64 signature proving the caller holds the private key. */
|
|
113
152
|
proof: string;
|
|
114
153
|
}
|
|
115
|
-
/** Returned by {@link VerdifaxClient.registerRequestKey}
|
|
154
|
+
/** Returned by {@link VerdifaxClient.registerRequestKey} and
|
|
155
|
+
* {@link VerdifaxClient.rotateRequestKey}. */
|
|
116
156
|
interface RequestKeyRegistration {
|
|
117
157
|
ok: boolean;
|
|
118
158
|
public_key: string;
|
|
@@ -120,8 +160,16 @@ interface RequestKeyRegistration {
|
|
|
120
160
|
interface ExecuteResponse {
|
|
121
161
|
ok: boolean;
|
|
122
162
|
run_id: number;
|
|
163
|
+
/** The record's hash: the manifest hash of a completed run. A refused or
|
|
164
|
+
* halted run throws an {@link APIError} carrying its `recordHash` and,
|
|
165
|
+
* when signed, its `signedRequest`. */
|
|
166
|
+
record_hash?: Hex64;
|
|
167
|
+
manifest_hash?: Hex64;
|
|
123
168
|
manifest: ExecutionManifest;
|
|
124
169
|
duration_ms: number;
|
|
170
|
+
/** The exact text that was signed, when the call was signed. Keep it to
|
|
171
|
+
* pass to `verdifax-verify --request FILE`. */
|
|
172
|
+
signed_request?: string;
|
|
125
173
|
}
|
|
126
174
|
/** One row of {@link ExecuteBatchResponse}. A successful row has
|
|
127
175
|
* `manifest`. A failed row has `error`, and `error_stage` and a typed
|
|
@@ -131,11 +179,16 @@ interface ExecuteBatchResult {
|
|
|
131
179
|
index: number;
|
|
132
180
|
ok: boolean;
|
|
133
181
|
run_id?: number;
|
|
182
|
+
/** The manifest hash of a completed row, or the receipt hash of a refused
|
|
183
|
+
* or halted one. */
|
|
184
|
+
record_hash?: Hex64;
|
|
134
185
|
manifest_hash?: Hex64;
|
|
135
186
|
manifest?: ExecutionManifest;
|
|
136
187
|
error?: string;
|
|
137
188
|
error_stage?: string;
|
|
138
189
|
duration_ms: number;
|
|
190
|
+
/** The exact text that was signed for this row, when it was signed. */
|
|
191
|
+
signed_request?: string;
|
|
139
192
|
partial_manifest?: ExecutionManifest;
|
|
140
193
|
deny_receipt?: Record<string, unknown>;
|
|
141
194
|
ccv_halt_receipt?: Record<string, unknown>;
|
|
@@ -176,6 +229,8 @@ interface VerifyDeterminismResponse {
|
|
|
176
229
|
first: DeterminismRun;
|
|
177
230
|
second: DeterminismRun;
|
|
178
231
|
diff: DeterminismDiff;
|
|
232
|
+
/** The exact text that was signed, when the call was signed. */
|
|
233
|
+
signed_request?: string;
|
|
179
234
|
}
|
|
180
235
|
/**
|
|
181
236
|
* Sealed manifest. Field names match the orchestrator's Go-default
|
|
@@ -284,6 +339,21 @@ interface AttestRequest {
|
|
|
284
339
|
authorization_chain?: Record<string, unknown>;
|
|
285
340
|
regulatory_scaffolding?: Record<string, unknown>;
|
|
286
341
|
causal_graph?: Record<string, unknown>;
|
|
342
|
+
/** Places this run in a job map. {@link VerdifaxJob.step} fills it in. */
|
|
343
|
+
job?: JobLink;
|
|
344
|
+
}
|
|
345
|
+
/** The second argument of {@link VerdifaxJob.step}: everything
|
|
346
|
+
* {@link VerdifaxClient.attest} takes except `job`, which the helper
|
|
347
|
+
* builds, plus the step's place in the job. */
|
|
348
|
+
interface JobStepOptions extends Omit<AttestRequest, "job"> {
|
|
349
|
+
/** Step ids in this job that this step directly follows. When omitted,
|
|
350
|
+
* the step follows the one run just before it through this helper. An
|
|
351
|
+
* empty list means it follows none. */
|
|
352
|
+
after?: string[];
|
|
353
|
+
/** Record hashes from another account that this step follows. */
|
|
354
|
+
external_previous?: Hex64[];
|
|
355
|
+
/** True on the step that closes the job. */
|
|
356
|
+
final?: boolean;
|
|
287
357
|
}
|
|
288
358
|
/** Optional filters for {@link VerdifaxClient.listRuns}. */
|
|
289
359
|
interface ListRunsParams {
|
|
@@ -325,6 +395,8 @@ interface SearchRunsParams {
|
|
|
325
395
|
interface ExportRunsParams {
|
|
326
396
|
from?: string;
|
|
327
397
|
to?: string;
|
|
398
|
+
/** Limits the export to the runs of one job. */
|
|
399
|
+
job_id?: string;
|
|
328
400
|
}
|
|
329
401
|
/** One run in an evidence export: what the server stored, not a verdict. */
|
|
330
402
|
interface ExportRow {
|
|
@@ -338,6 +410,11 @@ interface ExportRow {
|
|
|
338
410
|
leaf_hash: string;
|
|
339
411
|
complete_record_entry_id: string;
|
|
340
412
|
complete_record_leaf_hash: string;
|
|
413
|
+
/** Where the row's public-log entry was read from: `bundle`, `receipt`
|
|
414
|
+
* or `none`. */
|
|
415
|
+
anchor_source?: string;
|
|
416
|
+
job_id?: string;
|
|
417
|
+
step_id?: string;
|
|
341
418
|
}
|
|
342
419
|
/** Body returned by GET /runs/export in JSON form. */
|
|
343
420
|
interface RunsExport {
|
|
@@ -453,6 +530,47 @@ interface AdminAPIKey {
|
|
|
453
530
|
revoked: boolean;
|
|
454
531
|
}
|
|
455
532
|
|
|
533
|
+
/**
|
|
534
|
+
* The job helper: runs the steps of one job and keeps the map of step ids
|
|
535
|
+
* to record hashes, so a later step can name the steps it follows.
|
|
536
|
+
*/
|
|
537
|
+
|
|
538
|
+
/** Returned by {@link VerdifaxClient.job}.
|
|
539
|
+
*
|
|
540
|
+
* Every step goes through `attest`, so it signs when the client signs. A
|
|
541
|
+
* refused or halted step stays in the map and a later step can name it.
|
|
542
|
+
* A step that sealed nothing, for example a timeout or a 409, is not in the
|
|
543
|
+
* map and does not move the default `after`, so it can run again under the
|
|
544
|
+
* same id. An empty `after` starts another root step.
|
|
545
|
+
*
|
|
546
|
+
* `signedRequests` holds the payloads of signed steps, so it is a private
|
|
547
|
+
* field: logging a job prints the hashes and not that text.
|
|
548
|
+
*
|
|
549
|
+
* To run branches at the same time, give each its `after`: the default
|
|
550
|
+
* reads the last step that finished. */
|
|
551
|
+
declare class VerdifaxJob {
|
|
552
|
+
#private;
|
|
553
|
+
readonly jobId: string;
|
|
554
|
+
private readonly client;
|
|
555
|
+
private readonly hashes;
|
|
556
|
+
private lastStepId;
|
|
557
|
+
constructor(client: {
|
|
558
|
+
attest(req: AttestRequest): Promise<ExecuteResponse>;
|
|
559
|
+
}, jobId: string);
|
|
560
|
+
/** Step id to record hash for every step that left a record, on a copy. */
|
|
561
|
+
get recordHashes(): Record<string, Hex64>;
|
|
562
|
+
/** Step id to the exact text that was signed, for every signed step that
|
|
563
|
+
* left a record, refused and halted steps included, on a copy. Each text
|
|
564
|
+
* can be passed to `verdifax-verify --request FILE`. */
|
|
565
|
+
get signedRequests(): Record<string, string>;
|
|
566
|
+
/** Runs one step and records its hash and signed text. A refused or halted
|
|
567
|
+
* step records both from the error, then throws the same error `attest`
|
|
568
|
+
* throws. */
|
|
569
|
+
step(stepId: string, options: JobStepOptions): Promise<ExecuteResponse>;
|
|
570
|
+
private previousHashes;
|
|
571
|
+
private record;
|
|
572
|
+
}
|
|
573
|
+
|
|
456
574
|
/** The most rows POST /execute/batch accepts in one request. */
|
|
457
575
|
declare const MAX_BATCH_ROWS = 1000;
|
|
458
576
|
/**
|
|
@@ -496,14 +614,19 @@ declare const MAX_BATCH_ROWS = 1000;
|
|
|
496
614
|
* | `adminCreateKey(name)` | POST /admin/keys | required |
|
|
497
615
|
* | `adminListKeys()` | GET /admin/keys | required |
|
|
498
616
|
* | `adminRevokeKey(id)` | DELETE /admin/keys/{id} | required |
|
|
617
|
+
* | `adminClearRequestKey(id)` | DELETE /admin/keys/{id}/request-key | required |
|
|
499
618
|
* | `attestClaudeResponse(...)` | POST /execute (helper) | required |
|
|
500
619
|
* | `attestOpenAIResponse(...)` | POST /execute (helper) | required |
|
|
501
620
|
* | `registerRequestKey()` | POST /keys/request-key | required |
|
|
621
|
+
* | `rotateRequestKey(newSigningKey)` | POST /keys/request-key | required |
|
|
502
622
|
* | `clearRequestKey()` | DELETE /keys/request-key | required |
|
|
623
|
+
* | `job(jobId)` | none, returns a {@link VerdifaxJob} | none |
|
|
503
624
|
*
|
|
504
625
|
* With the `signingKey` option set, every path to POST /execute carries
|
|
505
626
|
* a fresh request signature, each row of a batch carries its own, and a
|
|
506
|
-
* determinism check carries one.
|
|
627
|
+
* determinism check carries one. Each sends `signed_request` and
|
|
628
|
+
* `request_signature` and nothing else, and the result carries the signed
|
|
629
|
+
* text as `signed_request`. Without it, requests are unsigned.
|
|
507
630
|
*
|
|
508
631
|
* Idempotent GETs that hit a transient failure (502/503/504/429)
|
|
509
632
|
* are retried with exponential backoff + full jitter. Non-
|
|
@@ -517,7 +640,7 @@ declare class VerdifaxClient {
|
|
|
517
640
|
private readonly userAgent;
|
|
518
641
|
private readonly maxRetries;
|
|
519
642
|
private readonly retryBaseDelayMs;
|
|
520
|
-
private
|
|
643
|
+
private signingKey;
|
|
521
644
|
constructor(config?: ClientConfig);
|
|
522
645
|
health(): Promise<Health>;
|
|
523
646
|
version(): Promise<VerdifaxVersion>;
|
|
@@ -528,13 +651,15 @@ declare class VerdifaxClient {
|
|
|
528
651
|
execute(req: ExecuteRequest): Promise<ExecuteResponse>;
|
|
529
652
|
/** Runs one payload twice on the server and compares the manifest
|
|
530
653
|
* hashes. One signature covers the request, and the server claims
|
|
531
|
-
* its nonce once. Both runs are recorded.
|
|
654
|
+
* its nonce once. Both runs are recorded. A run that happens twice
|
|
655
|
+
* cannot be one job step, so a `job` is refused here. */
|
|
532
656
|
verifyDeterminism(req: ExecuteRequest): Promise<VerifyDeterminismResponse>;
|
|
533
657
|
/** Sends up to {@link MAX_BATCH_ROWS} runs in one request. With a
|
|
534
658
|
* signing key each row carries its own signature and nonce. A row
|
|
535
659
|
* that fails comes back in `results` with `ok: false`, so the call
|
|
536
|
-
* does not throw for it. The rows are not checked here
|
|
537
|
-
*
|
|
660
|
+
* does not throw for it. The rows are not checked here, except a job
|
|
661
|
+
* block, which is refused before anything is sent: the server reports
|
|
662
|
+
* any other bad row in that row's result.
|
|
538
663
|
*
|
|
539
664
|
* This is one POST and it is never retried. After a timeout the
|
|
540
665
|
* server may have run some or all of the rows. */
|
|
@@ -544,12 +669,26 @@ declare class VerdifaxClient {
|
|
|
544
669
|
* string (for plain text). Validates the hex fields and
|
|
545
670
|
* routes the call through to the canonical /execute. */
|
|
546
671
|
attest(req: AttestRequest): Promise<ExecuteResponse>;
|
|
672
|
+
/** A helper that runs the steps of one job through {@link attest} and
|
|
673
|
+
* keeps each step's record hash, so a later step can name it. It makes
|
|
674
|
+
* no request until a step runs. */
|
|
675
|
+
job(jobId: string): VerdifaxJob;
|
|
547
676
|
/** Registers this client's public key on its API key, with the proof
|
|
548
677
|
* that the client holds the private key. From then on the server
|
|
549
678
|
* refuses a run on this API key without a request signature. */
|
|
550
679
|
registerRequestKey(): Promise<RequestKeyRegistration>;
|
|
680
|
+
/** Replaces the registered key with `newSigningKey`. The new key proves
|
|
681
|
+
* itself and this client's current key approves the change. After
|
|
682
|
+
* success this client signs with the new key. If the call fails it
|
|
683
|
+
* keeps the current key, including after a timeout, when the server may
|
|
684
|
+
* have made the change. */
|
|
685
|
+
rotateRequestKey(newSigningKey: Uint8Array | KeyObject): Promise<RequestKeyRegistration>;
|
|
551
686
|
/** Clears the registered public key, so the server stops requiring
|
|
552
|
-
* request signatures on this API key.
|
|
687
|
+
* request signatures on this API key. With a signing key the client
|
|
688
|
+
* sends that key's approval, which the server requires while a key is
|
|
689
|
+
* registered. After success the client drops its signing key and sends
|
|
690
|
+
* unsigned requests, because the server refuses a signed run on an API
|
|
691
|
+
* key with no registered key. If the call fails it keeps the key. */
|
|
553
692
|
clearRequestKey(): Promise<void>;
|
|
554
693
|
getRun(runId: number): Promise<Run>;
|
|
555
694
|
verifyRun(runId: number): Promise<VerifyResponse>;
|
|
@@ -608,6 +747,10 @@ declare class VerdifaxClient {
|
|
|
608
747
|
adminCreateKey(name: string): Promise<AdminCreateKeyResponse>;
|
|
609
748
|
adminListKeys(): Promise<AdminAPIKey[]>;
|
|
610
749
|
adminRevokeKey(keyId: number): Promise<void>;
|
|
750
|
+
/** Clears the request key on an API key whose holder lost it, after a
|
|
751
|
+
* manual check. It is the one way to clear a key without that key. The
|
|
752
|
+
* id can be any API key, so this client keeps its own signing key. */
|
|
753
|
+
adminClearRequestKey(keyId: number): Promise<void>;
|
|
611
754
|
/** Convenience for attesting an Anthropic Claude API response. With
|
|
612
755
|
* `govern: true` the response text also goes to the AIVP review as
|
|
613
756
|
* `ai_output_text`; that is off by default, so the text leaves your
|
|
@@ -619,6 +762,7 @@ declare class VerdifaxClient {
|
|
|
619
762
|
prompt: string;
|
|
620
763
|
response: string;
|
|
621
764
|
govern?: boolean;
|
|
765
|
+
job?: JobLink;
|
|
622
766
|
}): Promise<ExecuteResponse>;
|
|
623
767
|
/** Convenience for attesting an OpenAI API response. `govern` works as
|
|
624
768
|
* in {@link attestClaudeResponse}. */
|
|
@@ -629,6 +773,7 @@ declare class VerdifaxClient {
|
|
|
629
773
|
prompt: string;
|
|
630
774
|
response: string;
|
|
631
775
|
govern?: boolean;
|
|
776
|
+
job?: JobLink;
|
|
632
777
|
}): Promise<ExecuteResponse>;
|
|
633
778
|
private attestProviderResponse;
|
|
634
779
|
private request;
|
|
@@ -636,8 +781,8 @@ declare class VerdifaxClient {
|
|
|
636
781
|
private requestRaw;
|
|
637
782
|
private requireAPIKey;
|
|
638
783
|
private requireExecuteFields;
|
|
639
|
-
/** The request with its derived flags
|
|
640
|
-
*
|
|
784
|
+
/** The body to send, on a copy: the request with its derived flags or,
|
|
785
|
+
* with a signing key, the signed text with a fresh signature and nonce. */
|
|
641
786
|
private prepared;
|
|
642
787
|
}
|
|
643
788
|
|
|
@@ -749,8 +894,9 @@ declare function cresReceiptHash(fields: {
|
|
|
749
894
|
* 32-byte public key and a 32-byte private key. Nothing is sent to
|
|
750
895
|
* the server. Store the private key as a secret. */
|
|
751
896
|
declare function generateRequestKeyPair(): RequestKeyPair;
|
|
752
|
-
/** The bytes a request signature signs: the fields joined with
|
|
753
|
-
* zero bytes, matching the orchestrator's RequestSignaturePreimage.
|
|
897
|
+
/** The bytes a version 1 request signature signs: the fields joined with
|
|
898
|
+
* single zero bytes, matching the orchestrator's RequestSignaturePreimage.
|
|
899
|
+
* The client now sends version 2. See {@link requestSignaturePreimageV2}. */
|
|
754
900
|
declare function requestSignaturePreimage(fields: {
|
|
755
901
|
payload_hash: string;
|
|
756
902
|
program_id: string;
|
|
@@ -759,9 +905,37 @@ declare function requestSignaturePreimage(fields: {
|
|
|
759
905
|
signed_at: string;
|
|
760
906
|
nonce: string;
|
|
761
907
|
}): Buffer;
|
|
762
|
-
/**
|
|
763
|
-
*
|
|
764
|
-
*
|
|
908
|
+
/** The bytes a version 2 request signature signs: the fields joined with
|
|
909
|
+
* single zero bytes, matching the orchestrator's
|
|
910
|
+
* RequestSignaturePreimageV2. With no job, the job fields are empty and
|
|
911
|
+
* `final` is false. */
|
|
912
|
+
declare function requestSignaturePreimageV2(fields: {
|
|
913
|
+
body_hash: string;
|
|
914
|
+
payload_hash: string;
|
|
915
|
+
program_id: string;
|
|
916
|
+
route_id: string;
|
|
917
|
+
registry_record_hash: string;
|
|
918
|
+
ai_output_hash: string;
|
|
919
|
+
job?: JobLink;
|
|
920
|
+
signed_at: string;
|
|
921
|
+
nonce: string;
|
|
922
|
+
}): Buffer;
|
|
923
|
+
/** Signs one execute request with signature version 2 and returns the
|
|
924
|
+
* whole body to send. The request is serialized once, and that exact text
|
|
925
|
+
* is what is hashed, signed and sent.
|
|
926
|
+
*
|
|
927
|
+
* The payload hash follows the same rule as {@link signRequest}.
|
|
928
|
+
* `signedAt` and `nonce` default to the current time and fresh random
|
|
929
|
+
* bytes. A nonce the server has already seen is refused. */
|
|
930
|
+
declare function signRequestV2(signingKey: Uint8Array | KeyObject, req: ExecuteRequest, options?: {
|
|
931
|
+
signedAt?: string;
|
|
932
|
+
nonce?: string;
|
|
933
|
+
}): SignedRequestBody;
|
|
934
|
+
/** Signs one execute request with signature version 1, which the server
|
|
935
|
+
* still accepts. The client now sends version 2: see {@link signRequestV2}.
|
|
936
|
+
* The server hashes payload_text when it is non-empty, otherwise the
|
|
937
|
+
* payload string exactly as sent (it does not base64-decode it), so this
|
|
938
|
+
* does the same.
|
|
765
939
|
*
|
|
766
940
|
* `signedAt` and `nonce` default to the current time and fresh random
|
|
767
941
|
* bytes. A nonce the server has already seen is refused. */
|
|
@@ -772,6 +946,21 @@ declare function signRequest(signingKey: Uint8Array | KeyObject, req: ExecuteReq
|
|
|
772
946
|
/** The body of POST /keys/request-key: the base64 public key and a
|
|
773
947
|
* signature proving the caller holds its private key. */
|
|
774
948
|
declare function requestKeyRegistrationBody(signingKey: Uint8Array | KeyObject): RequestKeyRegistrationBody;
|
|
949
|
+
/** The bytes the current key signs to replace or clear itself. `new_public_key`
|
|
950
|
+
* is the new base64 public key, or empty when clearing. */
|
|
951
|
+
declare function requestKeyChangePreimage(fields: {
|
|
952
|
+
new_public_key: string;
|
|
953
|
+
signed_at: string;
|
|
954
|
+
nonce: string;
|
|
955
|
+
}): Buffer;
|
|
956
|
+
/** The current key's approval of a key change, the `change` value of POST
|
|
957
|
+
* and DELETE /keys/request-key. Pass an empty `newPublicKey` when clearing.
|
|
958
|
+
* `signedAt` and `nonce` default to the current time and fresh random
|
|
959
|
+
* bytes. */
|
|
960
|
+
declare function requestKeyChangeApproval(currentSigningKey: Uint8Array | KeyObject, newPublicKey: string, options?: {
|
|
961
|
+
signedAt?: string;
|
|
962
|
+
nonce?: string;
|
|
963
|
+
}): RequestKeyChange;
|
|
775
964
|
|
|
776
965
|
/**
|
|
777
966
|
* Input-validation helpers, mirrors the Python SDK's
|
|
@@ -791,6 +980,9 @@ declare function validateHex64(value: string, fieldName: string): void;
|
|
|
791
980
|
* a wider character set, but the SDK enforces the conservative
|
|
792
981
|
* subset by default to catch typos early. */
|
|
793
982
|
declare function validateRouteId(value: string): void;
|
|
983
|
+
/** Throws unless `value` is a job or step name: 1 to 128 letters, digits,
|
|
984
|
+
* dots, underscores, colons or dashes, the server's own rule. */
|
|
985
|
+
declare function validateJobName(value: string, fieldName: string): void;
|
|
794
986
|
|
|
795
987
|
/**
|
|
796
988
|
* Typed errors returned by the Verdifax SDK.
|
|
@@ -830,13 +1022,22 @@ declare class NotFoundError extends VerdifaxError {
|
|
|
830
1022
|
constructor(path: string, message: string);
|
|
831
1023
|
}
|
|
832
1024
|
/** Every other non-2xx response. Carries the HTTP status and
|
|
833
|
-
* the path so callers can switch on either dimension.
|
|
1025
|
+
* the path so callers can switch on either dimension. `recordHash` is
|
|
1026
|
+
* the receipt hash of a refused or halted run, when the server sealed
|
|
1027
|
+
* one; it is undefined when no record exists.
|
|
1028
|
+
*
|
|
1029
|
+
* `signedRequest` is the exact text that was signed, when the call was a
|
|
1030
|
+
* signed run. Keep it to pass to `verdifax-verify --request FILE`. It holds
|
|
1031
|
+
* the payload, so it stays out of the message and is not enumerable: read
|
|
1032
|
+
* it by name, and logging the error does not print it. */
|
|
834
1033
|
declare class APIError extends VerdifaxError {
|
|
835
1034
|
readonly status: number;
|
|
836
1035
|
readonly path: string;
|
|
837
1036
|
readonly message: string;
|
|
1037
|
+
readonly recordHash?: string | undefined;
|
|
838
1038
|
readonly name: string;
|
|
839
|
-
|
|
1039
|
+
readonly signedRequest?: string;
|
|
1040
|
+
constructor(status: number, path: string, message: string, recordHash?: string | undefined, signedRequest?: string);
|
|
840
1041
|
}
|
|
841
1042
|
/** Returned when an authenticated method is called on a client
|
|
842
1043
|
* constructed without an apiKey. */
|
|
@@ -845,4 +1046,4 @@ declare class APIKeyRequiredError extends VerdifaxError {
|
|
|
845
1046
|
constructor();
|
|
846
1047
|
}
|
|
847
1048
|
|
|
848
|
-
export { APIError, APIKeyRequiredError, type AdminAPIKey, type AdminCreateKeyResponse, type AdminEraseRequest, type AttestRequest, type AttestationList, AuthError, CRES_ENGINE_VERSION, CRES_PREIMAGE_VERSION, type ClientConfig, type DCAEProofBundle, type DCAEVerdict, DCAE_PREIMAGE_VERSION, DSE_FORMULA_VERSION, type DeletionReceipt, type DeterminismDiff, type DeterminismRun, type ExecuteBatchResponse, type ExecuteBatchResult, type ExecuteRequest, type ExecuteResponse, type ExecutionManifest, type ExportRow, type ExportRunsParams, type FormulaEra, type Health, type Hex64, type ListAttestationsParams, type ListRunsParams, MAX_BATCH_ROWS, NotFoundError, type PDFFormat, RUN_STATUSES, type ReproducibilityContext, type RequestKeyPair, type RequestKeyRegistration, type RequestKeyRegistrationBody, type RequestSignature, type Run, type RunStats, type RunStatus, type RunSummary, type RunsExport, type RunsExportCSV, type SearchRunsParams, VerdifaxClient, VerdifaxError, type VerdifaxVersion, type VerifyDeterminismResponse, type VerifyResponse, captureEnvironment, cresReceiptHash, dcaeClosureHash, dseDispatchHash, formatProviderPayload, fromEnv, generateRequestKeyPair, requestKeyRegistrationBody, requestSignaturePreimage, signRequest, validateHex64, validateRouteId };
|
|
1049
|
+
export { APIError, APIKeyRequiredError, type AdminAPIKey, type AdminCreateKeyResponse, type AdminEraseRequest, type AttestRequest, type AttestationList, AuthError, CRES_ENGINE_VERSION, CRES_PREIMAGE_VERSION, type ClientConfig, type DCAEProofBundle, type DCAEVerdict, DCAE_PREIMAGE_VERSION, DSE_FORMULA_VERSION, type DeletionReceipt, type DeterminismDiff, type DeterminismRun, type ExecuteBatchResponse, type ExecuteBatchResult, type ExecuteRequest, type ExecuteResponse, type ExecutionManifest, type ExportRow, type ExportRunsParams, type FormulaEra, type Health, type Hex64, type JobLink, type JobStepOptions, type ListAttestationsParams, type ListRunsParams, MAX_BATCH_ROWS, NotFoundError, type PDFFormat, RUN_STATUSES, type ReproducibilityContext, type RequestKeyChange, type RequestKeyPair, type RequestKeyRegistration, type RequestKeyRegistrationBody, type RequestSignature, type RequestSignatureV2, type Run, type RunStats, type RunStatus, type RunSummary, type RunsExport, type RunsExportCSV, type SearchRunsParams, type SignedRequestBody, VerdifaxClient, VerdifaxError, VerdifaxJob, type VerdifaxVersion, type VerifyDeterminismResponse, type VerifyResponse, captureEnvironment, cresReceiptHash, dcaeClosureHash, dseDispatchHash, formatProviderPayload, fromEnv, generateRequestKeyPair, requestKeyChangeApproval, requestKeyChangePreimage, requestKeyRegistrationBody, requestSignaturePreimage, requestSignaturePreimageV2, signRequest, signRequestV2, validateHex64, validateJobName, validateRouteId };
|