verdifax 0.3.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/dist/index.d.mts CHANGED
@@ -48,11 +48,64 @@ interface ExecuteRequest {
48
48
  registry_record_hash: Hex64;
49
49
  /** When non-empty, opts the request into the AIVP governance pipeline. */
50
50
  ai_output_text?: string;
51
- /** Caller-attested actor + decision metadata; open schema. */
51
+ /** Caller-attested actor + decision metadata; open schema. The client
52
+ * derives `attested` from whether any other field is provided. */
52
53
  attested_context?: Record<string, unknown>;
53
- /** Filled in by the client when the `signingKey` option is set. */
54
+ /** Caller-declared runtime fingerprint, recorded in the audit bundle.
55
+ * Build it with {@link captureEnvironment}. */
56
+ reproducibility_context?: ReproducibilityContext;
57
+ /** Caller-supplied substance of the request: prompt, output, cost,
58
+ * latency and origin metadata. Recorded as supplied. Leave out
59
+ * `origin.source_ip_hash` and `origin.user_agent_hash`: the server
60
+ * computes those and refuses them. */
61
+ request_substance?: Record<string, unknown>;
62
+ /** Caller-supplied approval chain. Recorded as supplied, not verified
63
+ * by Verdifax. */
64
+ authorization_chain?: Record<string, unknown>;
65
+ /** Caller-supplied regulatory metadata. Recorded as supplied, not an
66
+ * assessment by Verdifax. */
67
+ regulatory_scaffolding?: Record<string, unknown>;
68
+ /** Caller-supplied record of what led to this run. Recorded as
69
+ * supplied. */
70
+ causal_graph?: Record<string, unknown>;
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. */
54
76
  request_signature?: RequestSignature;
55
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
+ }
92
+ /** The runtime a caller declares for a run. Every field is optional.
93
+ * The server records these values as given and does not check them. */
94
+ interface ReproducibilityContext {
95
+ /** True when any other field has a value. The client derives this. */
96
+ declared?: boolean;
97
+ /** 64 hex characters, without the `sha256:` prefix. */
98
+ container_image_hash?: string;
99
+ runtime_name?: string;
100
+ runtime_version?: string;
101
+ /** `name==version` strings, sorted. */
102
+ pinned_dependencies?: string[];
103
+ git_commit_sha?: string;
104
+ /** `library=seed` strings, sorted. */
105
+ random_seeds?: string[];
106
+ /** GOOS/GOARCH style, for example `linux/amd64`. */
107
+ platform?: string;
108
+ }
56
109
  /** The signature an API key with a registered request key must carry.
57
110
  * The public key comes from the registration, never from the request. */
58
111
  interface RequestSignature {
@@ -63,6 +116,27 @@ interface RequestSignature {
63
116
  /** 16 random bytes as 32 lowercase hex characters, fresh per request. */
64
117
  nonce: string;
65
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
+ }
66
140
  /** A key pair from {@link generateRequestKeyPair}, as raw bytes. */
67
141
  interface RequestKeyPair {
68
142
  /** The 32-byte Ed25519 public key. */
@@ -77,7 +151,8 @@ interface RequestKeyRegistrationBody {
77
151
  /** Base64 signature proving the caller holds the private key. */
78
152
  proof: string;
79
153
  }
80
- /** Returned by {@link VerdifaxClient.registerRequestKey}. */
154
+ /** Returned by {@link VerdifaxClient.registerRequestKey} and
155
+ * {@link VerdifaxClient.rotateRequestKey}. */
81
156
  interface RequestKeyRegistration {
82
157
  ok: boolean;
83
158
  public_key: string;
@@ -85,8 +160,77 @@ interface RequestKeyRegistration {
85
160
  interface ExecuteResponse {
86
161
  ok: boolean;
87
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;
88
168
  manifest: ExecutionManifest;
89
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;
173
+ }
174
+ /** One row of {@link ExecuteBatchResponse}. A successful row has
175
+ * `manifest`. A failed row has `error`, and `error_stage` and a typed
176
+ * receipt when the pipeline halted. A row refused before it ran has
177
+ * no `run_id`. */
178
+ interface ExecuteBatchResult {
179
+ index: number;
180
+ ok: boolean;
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;
185
+ manifest_hash?: Hex64;
186
+ manifest?: ExecutionManifest;
187
+ error?: string;
188
+ error_stage?: string;
189
+ duration_ms: number;
190
+ /** The exact text that was signed for this row, when it was signed. */
191
+ signed_request?: string;
192
+ partial_manifest?: ExecutionManifest;
193
+ deny_receipt?: Record<string, unknown>;
194
+ ccv_halt_receipt?: Record<string, unknown>;
195
+ macc_halt_receipt?: Record<string, unknown>;
196
+ aivp_t4_halt_receipt?: Record<string, unknown>;
197
+ attestation_gap_receipt?: Record<string, unknown>;
198
+ }
199
+ /** Body returned by POST /execute/batch. */
200
+ interface ExecuteBatchResponse {
201
+ ok: boolean;
202
+ total: number;
203
+ successful: number;
204
+ failed: number;
205
+ duration_ms: number;
206
+ results: ExecuteBatchResult[];
207
+ }
208
+ /** One of the two runs of a determinism check. */
209
+ interface DeterminismRun {
210
+ run_id: number;
211
+ manifest_hash: string;
212
+ bundle_hash: string;
213
+ duration_ms: number;
214
+ status: string;
215
+ error?: string;
216
+ }
217
+ /** How the two runs of a determinism check compared. */
218
+ interface DeterminismDiff {
219
+ differs: boolean;
220
+ manifest_hash_differs: boolean;
221
+ bundle_hash_differs: boolean;
222
+ differing_fields?: string[];
223
+ }
224
+ /** Body returned by POST /execute/verify-determinism. `deterministic`
225
+ * means both runs ended ok with equal manifest hashes. */
226
+ interface VerifyDeterminismResponse {
227
+ ok: boolean;
228
+ deterministic: boolean;
229
+ first: DeterminismRun;
230
+ second: DeterminismRun;
231
+ diff: DeterminismDiff;
232
+ /** The exact text that was signed, when the call was signed. */
233
+ signed_request?: string;
90
234
  }
91
235
  /**
92
236
  * Sealed manifest. Field names match the orchestrator's Go-default
@@ -121,6 +265,12 @@ interface ExecutionManifest {
121
265
  LedgerProofHash?: Hex64;
122
266
  LogEntryID?: string;
123
267
  }
268
+ /** Every status the server gives a run. */
269
+ declare const RUN_STATUSES: readonly ["ok", "probe", "pepg_deny", "ccv_halt", "macc_halt", "aivp_t4_halt", "attestation_gap", "stage_error"];
270
+ /** A run's status: one of {@link RUN_STATUSES}, or a value a newer server
271
+ * adds. Written with `string & {}` so editors still offer the known
272
+ * values and any string can be compared. */
273
+ type RunStatus = (typeof RUN_STATUSES)[number] | (string & {});
124
274
  interface Run {
125
275
  ok: boolean;
126
276
  id: number;
@@ -130,7 +280,7 @@ interface Run {
130
280
  route_id: string;
131
281
  manifest_hash: Hex64;
132
282
  duration_ms: number;
133
- status: "ok" | "pepg_deny" | "ccv_halt" | "macc_halt" | "stage_error";
283
+ status: RunStatus;
134
284
  error_stage?: string;
135
285
  error_message?: string;
136
286
  manifest: ExecutionManifest;
@@ -184,6 +334,26 @@ interface AttestRequest {
184
334
  registry_record_hash: Hex64;
185
335
  ai_output_text?: string;
186
336
  attested_context?: Record<string, unknown>;
337
+ reproducibility_context?: ReproducibilityContext;
338
+ request_substance?: Record<string, unknown>;
339
+ authorization_chain?: Record<string, unknown>;
340
+ regulatory_scaffolding?: Record<string, unknown>;
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;
187
357
  }
188
358
  /** Optional filters for {@link VerdifaxClient.listRuns}. */
189
359
  interface ListRunsParams {
@@ -199,8 +369,101 @@ interface RunSummary {
199
369
  program_id: Hex64;
200
370
  route_id: string;
201
371
  manifest_hash: Hex64;
202
- status: "ok" | "pepg_deny" | "ccv_halt" | "macc_halt" | "stage_error";
372
+ status: RunStatus;
203
373
  outcome_kind?: string;
374
+ duration_ms?: number;
375
+ error_stage?: string;
376
+ execution_region?: string;
377
+ }
378
+ /** Optional filters for {@link VerdifaxClient.searchRuns}. `from` and
379
+ * `to` are RFC 3339 times. */
380
+ interface SearchRunsParams {
381
+ /** Matches manifest hashes that start with this value. */
382
+ manifest_hash?: string;
383
+ program_id?: Hex64;
384
+ route_id?: string;
385
+ status?: string;
386
+ from?: string;
387
+ to?: string;
388
+ limit?: number;
389
+ offset?: number;
390
+ }
391
+ /** Period for {@link VerdifaxClient.exportRuns} and
392
+ * {@link VerdifaxClient.exportRunsCSV}, as RFC 3339 times. `from` is
393
+ * inclusive and `to` is exclusive. `to` defaults to the time of the
394
+ * request. */
395
+ interface ExportRunsParams {
396
+ from?: string;
397
+ to?: string;
398
+ /** Limits the export to the runs of one job. */
399
+ job_id?: string;
400
+ }
401
+ /** One run in an evidence export: what the server stored, not a verdict. */
402
+ interface ExportRow {
403
+ run_id: number;
404
+ created_at: string;
405
+ status: RunStatus;
406
+ error_stage: string;
407
+ route_id: string;
408
+ manifest_hash: string;
409
+ log_entry_id: string;
410
+ leaf_hash: string;
411
+ complete_record_entry_id: string;
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;
418
+ }
419
+ /** Body returned by GET /runs/export in JSON form. */
420
+ interface RunsExport {
421
+ ok: boolean;
422
+ /** The upper bound the server used for this export. */
423
+ to: string;
424
+ /** Runs held for the period. */
425
+ total: number;
426
+ /** Rows in this response. */
427
+ returned: number;
428
+ /** True when the period holds more rows than this response. */
429
+ truncated: boolean;
430
+ /** Rows returned, counted by status. */
431
+ by_status: Record<string, number>;
432
+ runs: ExportRow[];
433
+ }
434
+ /** Returned by {@link VerdifaxClient.exportRunsCSV}. */
435
+ interface RunsExportCSV {
436
+ /** The CSV text, unparsed. */
437
+ csv: string;
438
+ /** Runs held for the period. */
439
+ total: number;
440
+ /** True when the period holds more rows than `csv`. */
441
+ truncated: boolean;
442
+ }
443
+ /** Optional paging for {@link VerdifaxClient.listAttestations}. */
444
+ interface ListAttestationsParams {
445
+ limit?: number;
446
+ offset?: number;
447
+ }
448
+ /** Body returned by GET /attestations. Each row is a receipt as the
449
+ * server sends it, without its inclusion proof. */
450
+ interface AttestationList {
451
+ ok: boolean;
452
+ count: number;
453
+ limit: number;
454
+ offset: number;
455
+ attestations: Record<string, unknown>[];
456
+ }
457
+ /** Body returned by GET /stats. */
458
+ interface RunStats {
459
+ ok: boolean;
460
+ total_runs: number;
461
+ runs_today: number;
462
+ runs_last_24h: number;
463
+ pass_rate: number;
464
+ avg_duration_ms: number;
465
+ last_run_at: string;
466
+ runs_by_program: Record<string, number>;
204
467
  }
205
468
  /** Audit-PDF format selector. */
206
469
  type PDFFormat = "brief" | "auditor" | "legal" | "comprehensive";
@@ -267,6 +530,49 @@ interface AdminAPIKey {
267
530
  revoked: boolean;
268
531
  }
269
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
+
574
+ /** The most rows POST /execute/batch accepts in one request. */
575
+ declare const MAX_BATCH_ROWS = 1000;
270
576
  /**
271
577
  * The Verdifax SDK client.
272
578
  *
@@ -276,30 +582,51 @@ interface AdminAPIKey {
276
582
  * | --- | --- | --- |
277
583
  * | `health()` | GET /health | none |
278
584
  * | `version()` | GET /version | none |
585
+ * | `getOpenAPI()` | GET /openapi.json | none |
586
+ * | `getStats()` | GET /stats | required |
279
587
  * | `execute(req)` | POST /execute | required |
280
588
  * | `attest(req)` | POST /execute (helper) | required |
281
589
  * | `getRun(id)` | GET /runs/{id} | required |
282
590
  * | `verifyRun(id)` | GET /runs/{id}/verify | required |
283
591
  * | `listRuns(params)` | GET /runs | required |
592
+ * | `searchRuns(params)` | GET /runs/search | required |
593
+ * | `exportRuns(params)` | GET /runs/export | required |
594
+ * | `exportRunsCSV(params)` | GET /runs/export?format=csv | required |
595
+ * | `listAttestations(params)` | GET /attestations | required |
596
+ * | `getAttestation(id)` | GET /attestations/{id} | required |
597
+ * | `getRunArtifacts(id)` | GET /runs/{id}/artifacts | required |
598
+ * | `getRunReport(id)` | GET /runs/{id}/report | required |
599
+ * | `getPublicBundle(id)` | GET /runs/{id}/bundle.json | none |
600
+ * | `getPublicBundleByToken(token)` | GET /public/runs/{token}/bundle.json | none |
601
+ * | `executeBatch(runs)` | POST /execute/batch | required |
602
+ * | `verifyDeterminism(req)` | POST /execute/verify-determinism | required |
284
603
  * | `downloadAuditPDF(id, format)` | GET /runs/{id}/report.pdf | required |
285
604
  * | `getAllowToken(id)` | GET /runs/{id}/allow-token | required |
286
605
  * | `getDenyReceipt(id)` | GET /runs/{id}/deny-receipt | required |
287
606
  * | `getCCVHaltReceipt(id)` | GET /runs/{id}/ccv-halt-receipt | required |
288
607
  * | `getMACCHaltReceipt(id)` | GET /runs/{id}/macc-halt-receipt | required |
289
608
  * | `getAivpT4HaltReceipt(id)` | GET /runs/{id}/aivp-t4-halt-receipt | required |
609
+ * | `getAttestationGapReceipt(id)` | GET /runs/{id}/attestation-gap-receipt | required |
610
+ * | `getCESAuditRecord(id)` | GET /runs/{id}/ces-audit-record | required |
290
611
  * | `listDeletionReceipts(id)` | GET /runs/{id}/deletion-receipts | required |
291
612
  * | `adminErase(req)` | POST /admin/erase | required |
292
613
  * | `dcaeVerify(bundle)` | POST /dcae/verify | none |
293
614
  * | `adminCreateKey(name)` | POST /admin/keys | required |
294
615
  * | `adminListKeys()` | GET /admin/keys | required |
295
616
  * | `adminRevokeKey(id)` | DELETE /admin/keys/{id} | required |
617
+ * | `adminClearRequestKey(id)` | DELETE /admin/keys/{id}/request-key | required |
296
618
  * | `attestClaudeResponse(...)` | POST /execute (helper) | required |
297
619
  * | `attestOpenAIResponse(...)` | POST /execute (helper) | required |
298
620
  * | `registerRequestKey()` | POST /keys/request-key | required |
621
+ * | `rotateRequestKey(newSigningKey)` | POST /keys/request-key | required |
299
622
  * | `clearRequestKey()` | DELETE /keys/request-key | required |
623
+ * | `job(jobId)` | none, returns a {@link VerdifaxJob} | none |
300
624
  *
301
625
  * With the `signingKey` option set, every path to POST /execute carries
302
- * a fresh request signature. Without it, requests are unsigned.
626
+ * a fresh request signature, each row of a batch carries its own, and a
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.
303
630
  *
304
631
  * Idempotent GETs that hit a transient failure (502/503/504/429)
305
632
  * are retried with exponential backoff + full jitter. Non-
@@ -313,22 +640,55 @@ declare class VerdifaxClient {
313
640
  private readonly userAgent;
314
641
  private readonly maxRetries;
315
642
  private readonly retryBaseDelayMs;
316
- private readonly signingKey;
643
+ private signingKey;
317
644
  constructor(config?: ClientConfig);
318
645
  health(): Promise<Health>;
319
646
  version(): Promise<VerdifaxVersion>;
647
+ /** The server's own API description. */
648
+ getOpenAPI(): Promise<Record<string, unknown>>;
649
+ /** Run counts for this API key. */
650
+ getStats(): Promise<RunStats>;
320
651
  execute(req: ExecuteRequest): Promise<ExecuteResponse>;
652
+ /** Runs one payload twice on the server and compares the manifest
653
+ * hashes. One signature covers the request, and the server claims
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. */
656
+ verifyDeterminism(req: ExecuteRequest): Promise<VerifyDeterminismResponse>;
657
+ /** Sends up to {@link MAX_BATCH_ROWS} runs in one request. With a
658
+ * signing key each row carries its own signature and nonce. A row
659
+ * that fails comes back in `results` with `ok: false`, so the call
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.
663
+ *
664
+ * This is one POST and it is never retried. After a timeout the
665
+ * server may have run some or all of the rows. */
666
+ executeBatch(runs: ExecuteRequest[]): Promise<ExecuteBatchResponse>;
321
667
  /** High-level convenience wrapper around `execute()`. Accepts
322
668
  * either a Buffer (for binary payloads, auto-base64) or a
323
669
  * string (for plain text). Validates the hex fields and
324
670
  * routes the call through to the canonical /execute. */
325
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;
326
676
  /** Registers this client's public key on its API key, with the proof
327
677
  * that the client holds the private key. From then on the server
328
678
  * refuses a run on this API key without a request signature. */
329
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>;
330
686
  /** Clears the registered public key, so the server stops requiring
331
- * 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. */
332
692
  clearRequestKey(): Promise<void>;
333
693
  getRun(runId: number): Promise<Run>;
334
694
  verifyRun(runId: number): Promise<VerifyResponse>;
@@ -336,11 +696,47 @@ declare class VerdifaxClient {
336
696
  runs: RunSummary[];
337
697
  total: number;
338
698
  }>;
699
+ /** Searches this key's runs. `manifest_hash` matches a prefix. The
700
+ * server ignores a `from` or `to` it cannot parse here, unlike
701
+ * {@link exportRuns}, which refuses one. */
702
+ searchRuns(params?: SearchRunsParams): Promise<{
703
+ runs: RunSummary[];
704
+ total: number;
705
+ }>;
706
+ /** Lists what the server holds for this key in a period, failures
707
+ * included, up to 10,000 rows. `truncated` is true when the period
708
+ * holds more. The list states what was stored, not a verdict, and a
709
+ * call that never reached Verdifax cannot appear in it. */
710
+ exportRuns(params?: ExportRunsParams): Promise<RunsExport>;
711
+ /** The same rows as {@link exportRuns}: the raw CSV text with `total`
712
+ * and `truncated` from the response headers. Check `truncated`: when
713
+ * true, the CSV is missing rows for the period. Throws when the server
714
+ * does not send those headers, so that a cut-off export cannot pass
715
+ * for a complete one. */
716
+ exportRunsCSV(params?: ExportRunsParams): Promise<RunsExportCSV>;
717
+ private exportPath;
718
+ listAttestations(params?: ListAttestationsParams): Promise<AttestationList>;
719
+ getAttestation(attestationId: number): Promise<Record<string, unknown>>;
339
720
  getAllowToken(runId: number): Promise<Record<string, unknown>>;
340
721
  getDenyReceipt(runId: number): Promise<Record<string, unknown>>;
341
722
  getCCVHaltReceipt(runId: number): Promise<Record<string, unknown>>;
342
723
  getMACCHaltReceipt(runId: number): Promise<Record<string, unknown>>;
343
724
  getAivpT4HaltReceipt(runId: number): Promise<Record<string, unknown>>;
725
+ getAttestationGapReceipt(runId: number): Promise<Record<string, unknown>>;
726
+ getCESAuditRecord(runId: number): Promise<Record<string, unknown>>;
727
+ /** The audit report for a run, as JSON. */
728
+ getRunReport(runId: number): Promise<Record<string, unknown>>;
729
+ /** The run's sealed audit bundle as the exact JSON text the server
730
+ * sent, to save and check with `verdifax-verify`. It is not parsed:
731
+ * JSON.parse would round a number above 2^53 and the recomputed hash
732
+ * would stop matching. */
733
+ getRunArtifacts(runId: number): Promise<string>;
734
+ /** The public audit bundle of a record published by run id, as the
735
+ * exact JSON text the server sent. Needs no API key. */
736
+ getPublicBundle(runId: number): Promise<string>;
737
+ /** The public audit bundle a share token points to, as the exact JSON
738
+ * text the server sent. Needs no API key. */
739
+ getPublicBundleByToken(token: string): Promise<string>;
344
740
  private getRunArtifact;
345
741
  /** Streams the audit PDF bytes as a Buffer. Format defaults to
346
742
  * "comprehensive" when not specified. */
@@ -351,27 +747,43 @@ declare class VerdifaxClient {
351
747
  adminCreateKey(name: string): Promise<AdminCreateKeyResponse>;
352
748
  adminListKeys(): Promise<AdminAPIKey[]>;
353
749
  adminRevokeKey(keyId: number): Promise<void>;
354
- /** Convenience for attesting an Anthropic Claude API response.
355
- * Wires the response text into the AIVP governance pipeline
356
- * by setting `ai_output_text`. */
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>;
754
+ /** Convenience for attesting an Anthropic Claude API response. With
755
+ * `govern: true` the response text also goes to the AIVP review as
756
+ * `ai_output_text`; that is off by default, so the text leaves your
757
+ * machine for review only when you ask. */
357
758
  attestClaudeResponse(args: {
358
759
  program_id: Hex64;
359
760
  route_id: string;
360
761
  registry_record_hash: Hex64;
361
762
  prompt: string;
362
763
  response: string;
764
+ govern?: boolean;
765
+ job?: JobLink;
363
766
  }): Promise<ExecuteResponse>;
364
- /** Convenience for attesting an OpenAI API response. */
767
+ /** Convenience for attesting an OpenAI API response. `govern` works as
768
+ * in {@link attestClaudeResponse}. */
365
769
  attestOpenAIResponse(args: {
366
770
  program_id: Hex64;
367
771
  route_id: string;
368
772
  registry_record_hash: Hex64;
369
773
  prompt: string;
370
774
  response: string;
775
+ govern?: boolean;
776
+ job?: JobLink;
371
777
  }): Promise<ExecuteResponse>;
372
778
  private attestProviderResponse;
373
779
  private request;
780
+ /** A GET whose body is kept as sent, retried like any other GET. */
781
+ private requestRaw;
374
782
  private requireAPIKey;
783
+ private requireExecuteFields;
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. */
786
+ private prepared;
375
787
  }
376
788
 
377
789
  /**
@@ -380,7 +792,9 @@ declare class VerdifaxClient {
380
792
  *
381
793
  * Variables consumed (identical names across SDKs):
382
794
  *
383
- * VERDIFAX_BASE_URL base URL (defaults to https://api.verdifax.com)
795
+ * VERDIFAX_BASE_URL base URL, read first
796
+ * VERDIFAX_API_URL base URL, read when VERDIFAX_BASE_URL is unset
797
+ * (defaults to https://api.verdifax.com)
384
798
  * VERDIFAX_API_KEY X-Verdifax-Key value
385
799
  * VERDIFAX_TIMEOUT_MS per-request timeout in ms (defaults to 90000)
386
800
  * VERDIFAX_USER_AGENT override the default User-Agent
@@ -389,6 +803,84 @@ declare class VerdifaxClient {
389
803
 
390
804
  declare function fromEnv(): VerdifaxClient;
391
805
 
806
+ /**
807
+ * Best-effort detection of the running Node environment, for the
808
+ * `reproducibility_context` of a run. A value that cannot be read is
809
+ * left out, and the server records it as not declared.
810
+ */
811
+
812
+ interface CaptureEnvironmentOptions {
813
+ /** Library name to seed value. Recorded as declared; the caller still
814
+ * has to apply the seeds. */
815
+ declaredSeeds?: Record<string, string | number>;
816
+ /** List the packages under `node_modules` in the working directory.
817
+ * Defaults to true. */
818
+ includeDependencies?: boolean;
819
+ /** Read the commit with `git rev-parse HEAD`. Defaults to true. */
820
+ includeGit?: boolean;
821
+ /** Read the container id from `/proc/self/cgroup` on Linux. Defaults
822
+ * to true. */
823
+ includeContainer?: boolean;
824
+ }
825
+ /** Auto-detects the running Node environment. */
826
+ declare function captureEnvironment(options?: CaptureEnvironmentOptions): ReproducibilityContext;
827
+
828
+ /**
829
+ * Request-body rules shared by every path to POST /execute.
830
+ */
831
+
832
+ /**
833
+ * The payload the provider helpers attest: canonical JSON with sorted keys,
834
+ * no extra whitespace and UTF-8 text, exactly
835
+ * `{"prompt":...,"provider":...,"response":...}`. All four SDKs produce the
836
+ * same bytes, and a shared test vector holds them to it.
837
+ *
838
+ * Text with an unpaired surrogate has no UTF-8 form and is refused.
839
+ */
840
+ declare function formatProviderPayload(provider: string, prompt: string, response: string): string;
841
+
842
+ /**
843
+ * The three canonical hash formulas from the Verdifax orchestrator, so a
844
+ * caller can recompute a DSE dispatch hash, a DCAE closure hash or a CRES
845
+ * receipt hash without calling the server.
846
+ *
847
+ * These match the Python SDK's `verdifax.section0` and pass its test
848
+ * vectors. Every hash is SHA-256 as 64 lowercase hex characters, over
849
+ * UTF-8 text.
850
+ */
851
+
852
+ declare const DSE_FORMULA_VERSION = "dse.tcu.dispatch.v1";
853
+ declare const DCAE_PREIMAGE_VERSION = "verdifax.dcae.v1";
854
+ declare const CRES_PREIMAGE_VERSION = "verdifax.cres.v1";
855
+ declare const CRES_ENGINE_VERSION = "verdifax-cres/1.0.0";
856
+ /** The DSE formula joins its fields with a period. */
857
+ declare function dseDispatchHash(fields: {
858
+ envelope_id: string;
859
+ sequence_id: string;
860
+ program_id: Hex64;
861
+ registry_record_hash: Hex64;
862
+ }): Hex64;
863
+ /** The DCAE formula joins its fields with a pipe. */
864
+ declare function dcaeClosureHash(fields: {
865
+ manifest_hash: Hex64;
866
+ aer_hash: Hex64;
867
+ envelope_id: string;
868
+ zksp_binding_hash: Hex64;
869
+ formal_verifier_status: string;
870
+ }): Hex64;
871
+ /** The CRES formula joins its fields with a pipe. `engine_version`
872
+ * defaults to {@link CRES_ENGINE_VERSION}; pass another value only to
873
+ * recompute a receipt from an earlier engine. */
874
+ declare function cresReceiptHash(fields: {
875
+ envelope_id: string;
876
+ field_path: string;
877
+ deletion_clock: string;
878
+ actor_id: string;
879
+ ciphertext_hash_at_shred: Hex64;
880
+ dsar_reference: string;
881
+ engine_version?: string;
882
+ }): Hex64;
883
+
392
884
  /**
393
885
  * Optional customer request signing.
394
886
  *
@@ -402,8 +894,9 @@ declare function fromEnv(): VerdifaxClient;
402
894
  * 32-byte public key and a 32-byte private key. Nothing is sent to
403
895
  * the server. Store the private key as a secret. */
404
896
  declare function generateRequestKeyPair(): RequestKeyPair;
405
- /** The bytes a request signature signs: the fields joined with single
406
- * 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}. */
407
900
  declare function requestSignaturePreimage(fields: {
408
901
  payload_hash: string;
409
902
  program_id: string;
@@ -412,9 +905,37 @@ declare function requestSignaturePreimage(fields: {
412
905
  signed_at: string;
413
906
  nonce: string;
414
907
  }): Buffer;
415
- /** Signs one execute request. The server hashes payload_text when it is
416
- * non-empty, otherwise the payload string exactly as sent (it does not
417
- * base64-decode it), so this does the same.
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.
418
939
  *
419
940
  * `signedAt` and `nonce` default to the current time and fresh random
420
941
  * bytes. A nonce the server has already seen is refused. */
@@ -425,6 +946,21 @@ declare function signRequest(signingKey: Uint8Array | KeyObject, req: ExecuteReq
425
946
  /** The body of POST /keys/request-key: the base64 public key and a
426
947
  * signature proving the caller holds its private key. */
427
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;
428
964
 
429
965
  /**
430
966
  * Input-validation helpers, mirrors the Python SDK's
@@ -444,6 +980,9 @@ declare function validateHex64(value: string, fieldName: string): void;
444
980
  * a wider character set, but the SDK enforces the conservative
445
981
  * subset by default to catch typos early. */
446
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;
447
986
 
448
987
  /**
449
988
  * Typed errors returned by the Verdifax SDK.
@@ -483,13 +1022,22 @@ declare class NotFoundError extends VerdifaxError {
483
1022
  constructor(path: string, message: string);
484
1023
  }
485
1024
  /** Every other non-2xx response. Carries the HTTP status and
486
- * 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. */
487
1033
  declare class APIError extends VerdifaxError {
488
1034
  readonly status: number;
489
1035
  readonly path: string;
490
1036
  readonly message: string;
1037
+ readonly recordHash?: string | undefined;
491
1038
  readonly name: string;
492
- constructor(status: number, path: string, message: string);
1039
+ readonly signedRequest?: string;
1040
+ constructor(status: number, path: string, message: string, recordHash?: string | undefined, signedRequest?: string);
493
1041
  }
494
1042
  /** Returned when an authenticated method is called on a client
495
1043
  * constructed without an apiKey. */
@@ -498,4 +1046,4 @@ declare class APIKeyRequiredError extends VerdifaxError {
498
1046
  constructor();
499
1047
  }
500
1048
 
501
- export { APIError, APIKeyRequiredError, type AdminAPIKey, type AdminCreateKeyResponse, type AdminEraseRequest, type AttestRequest, AuthError, type ClientConfig, type DCAEProofBundle, type DCAEVerdict, type DeletionReceipt, type ExecuteRequest, type ExecuteResponse, type ExecutionManifest, type FormulaEra, type Health, type Hex64, type ListRunsParams, NotFoundError, type PDFFormat, type RequestKeyPair, type RequestKeyRegistration, type RequestKeyRegistrationBody, type RequestSignature, type Run, type RunSummary, VerdifaxClient, VerdifaxError, type VerdifaxVersion, type VerifyResponse, 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 };