verdifax 0.2.11 → 1.0.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.js CHANGED
@@ -23,10 +23,25 @@ __export(index_exports, {
23
23
  APIError: () => APIError,
24
24
  APIKeyRequiredError: () => APIKeyRequiredError,
25
25
  AuthError: () => AuthError,
26
+ CRES_ENGINE_VERSION: () => CRES_ENGINE_VERSION,
27
+ CRES_PREIMAGE_VERSION: () => CRES_PREIMAGE_VERSION,
28
+ DCAE_PREIMAGE_VERSION: () => DCAE_PREIMAGE_VERSION,
29
+ DSE_FORMULA_VERSION: () => DSE_FORMULA_VERSION,
30
+ MAX_BATCH_ROWS: () => MAX_BATCH_ROWS,
26
31
  NotFoundError: () => NotFoundError,
32
+ RUN_STATUSES: () => RUN_STATUSES,
27
33
  VerdifaxClient: () => VerdifaxClient,
28
34
  VerdifaxError: () => VerdifaxError,
35
+ captureEnvironment: () => captureEnvironment,
36
+ cresReceiptHash: () => cresReceiptHash,
37
+ dcaeClosureHash: () => dcaeClosureHash,
38
+ dseDispatchHash: () => dseDispatchHash,
39
+ formatProviderPayload: () => formatProviderPayload,
29
40
  fromEnv: () => fromEnv,
41
+ generateRequestKeyPair: () => generateRequestKeyPair,
42
+ requestKeyRegistrationBody: () => requestKeyRegistrationBody,
43
+ requestSignaturePreimage: () => requestSignaturePreimage,
44
+ signRequest: () => signRequest,
30
45
  validateHex64: () => validateHex64,
31
46
  validateRouteId: () => validateRouteId
32
47
  });
@@ -75,6 +90,127 @@ var APIKeyRequiredError = class extends VerdifaxError {
75
90
  }
76
91
  };
77
92
 
93
+ // src/signing.ts
94
+ var import_node_crypto = require("crypto");
95
+ var REQUEST_SIGNATURE_FORM = "verdifax.request.v1";
96
+ var REQUEST_KEY_PROOF_FORM = "verdifax.request-key.v1";
97
+ var SIGNING_KEY_BYTES = 32;
98
+ var PKCS8_ED25519_HEADER = Buffer.from(
99
+ "302e020100300506032b657004220420",
100
+ "hex"
101
+ );
102
+ function rawPublicKey(key) {
103
+ return Buffer.from(key.export({ format: "der", type: "spki" }).subarray(-32));
104
+ }
105
+ function generateRequestKeyPair() {
106
+ const { publicKey, privateKey } = (0, import_node_crypto.generateKeyPairSync)("ed25519");
107
+ return {
108
+ publicKey: rawPublicKey(publicKey),
109
+ privateKey: Buffer.from(
110
+ privateKey.export({ format: "der", type: "pkcs8" }).subarray(-32)
111
+ )
112
+ };
113
+ }
114
+ function loadSigningKey(key) {
115
+ if (key instanceof import_node_crypto.KeyObject) return key;
116
+ if (!(key instanceof Uint8Array) || key.length !== SIGNING_KEY_BYTES) {
117
+ throw new VerdifaxError(
118
+ "verdifax: signingKey must be the 32 raw bytes of an Ed25519 private key"
119
+ );
120
+ }
121
+ return (0, import_node_crypto.createPrivateKey)({
122
+ key: Buffer.concat([PKCS8_ED25519_HEADER, key]),
123
+ format: "der",
124
+ type: "pkcs8"
125
+ });
126
+ }
127
+ function requestSignaturePreimage(fields) {
128
+ return Buffer.from(
129
+ [
130
+ REQUEST_SIGNATURE_FORM,
131
+ fields.payload_hash,
132
+ fields.program_id,
133
+ fields.route_id,
134
+ fields.registry_record_hash,
135
+ fields.signed_at,
136
+ fields.nonce
137
+ ].join("\0"),
138
+ "utf8"
139
+ );
140
+ }
141
+ function signRequest(signingKey, req, options = {}) {
142
+ const key = loadSigningKey(signingKey);
143
+ const signed_at = options.signedAt ?? (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "Z");
144
+ const nonce = options.nonce ?? (0, import_node_crypto.randomBytes)(16).toString("hex");
145
+ const payload_hash = (0, import_node_crypto.createHash)("sha256").update(Buffer.from(req.payload_text || req.payload || "", "utf8")).digest("hex");
146
+ const preimage = requestSignaturePreimage({
147
+ payload_hash,
148
+ program_id: req.program_id,
149
+ route_id: req.route_id,
150
+ registry_record_hash: req.registry_record_hash,
151
+ signed_at,
152
+ nonce
153
+ });
154
+ return {
155
+ signature: (0, import_node_crypto.sign)(null, preimage, key).toString("base64"),
156
+ signed_at,
157
+ nonce
158
+ };
159
+ }
160
+ function requestKeyRegistrationBody(signingKey) {
161
+ const key = loadSigningKey(signingKey);
162
+ const public_key = rawPublicKey((0, import_node_crypto.createPublicKey)(key)).toString("base64");
163
+ const proof = (0, import_node_crypto.sign)(
164
+ null,
165
+ Buffer.from(`${REQUEST_KEY_PROOF_FORM}\0${public_key}`, "utf8"),
166
+ key
167
+ ).toString("base64");
168
+ return { public_key, proof };
169
+ }
170
+
171
+ // src/helpers.ts
172
+ function formatProviderPayload(provider, prompt, response) {
173
+ if ([provider, prompt, response].some((text) => /[\uD800-\uDFFF]/u.test(text))) {
174
+ throw new VerdifaxError(
175
+ "verdifax: provider payload text cannot be encoded as UTF-8"
176
+ );
177
+ }
178
+ return JSON.stringify({ prompt, provider, response });
179
+ }
180
+ function isProvided(value) {
181
+ return value !== null && value !== void 0 && value !== "" && value !== false;
182
+ }
183
+ function withAttestedFlag(context) {
184
+ const provided = Object.entries(context).some(
185
+ ([name, value]) => name !== "attested" && isProvided(value)
186
+ );
187
+ return { ...context, attested: provided };
188
+ }
189
+ function withDeclaredFlag(context) {
190
+ const declared = Object.entries(context).some(
191
+ ([name, value]) => name !== "declared" && isProvided(value) && !(Array.isArray(value) && value.length === 0)
192
+ );
193
+ return { ...context, declared };
194
+ }
195
+ function refuseCallerOriginHashes(substance) {
196
+ const origin = substance?.origin;
197
+ if (origin?.source_ip_hash || origin?.user_agent_hash) {
198
+ throw new VerdifaxError(
199
+ "verdifax: request_substance.origin.source_ip_hash and user_agent_hash are computed by the server and cannot be supplied"
200
+ );
201
+ }
202
+ }
203
+ function withExecuteFlags(req) {
204
+ const body = { ...req };
205
+ if (req.attested_context) {
206
+ body.attested_context = withAttestedFlag(req.attested_context);
207
+ }
208
+ if (req.reproducibility_context) {
209
+ body.reproducibility_context = withDeclaredFlag(req.reproducibility_context);
210
+ }
211
+ return body;
212
+ }
213
+
78
214
  // src/validation.ts
79
215
  function validateHex64(value, fieldName) {
80
216
  if (typeof value !== "string" || value.length !== 64) {
@@ -105,7 +241,7 @@ function validateRouteId(value) {
105
241
  }
106
242
 
107
243
  // src/version.ts
108
- var SDK_VERSION = "0.2.11";
244
+ var SDK_VERSION = "1.0.0";
109
245
 
110
246
  // src/client.ts
111
247
  var DEFAULT_BASE_URL = "https://api.verdifax.com";
@@ -114,7 +250,11 @@ var DEFAULT_MAX_RETRIES = 3;
114
250
  var DEFAULT_RETRY_BASE_DELAY_MS = 200;
115
251
  var SDK_USER_AGENT = `verdifax-sdk-node/${SDK_VERSION}`;
116
252
  var HEADER_API_KEY = "X-Verdifax-Key";
253
+ var REQUEST_KEY_PATH = "/keys/request-key";
254
+ var HEADER_EXPORT_TOTAL = "X-Verdifax-Total";
255
+ var HEADER_EXPORT_TRUNCATED = "X-Verdifax-Truncated";
117
256
  var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
257
+ var MAX_BATCH_ROWS = 1e3;
118
258
  var VerdifaxClient = class {
119
259
  baseURL;
120
260
  apiKey;
@@ -122,6 +262,7 @@ var VerdifaxClient = class {
122
262
  userAgent;
123
263
  maxRetries;
124
264
  retryBaseDelayMs;
265
+ signingKey;
125
266
  constructor(config = {}) {
126
267
  this.baseURL = (config.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "");
127
268
  this.apiKey = config.apiKey ?? "";
@@ -129,6 +270,7 @@ var VerdifaxClient = class {
129
270
  this.userAgent = config.userAgent ?? SDK_USER_AGENT;
130
271
  this.maxRetries = config.maxRetries === void 0 ? DEFAULT_MAX_RETRIES : Math.max(0, config.maxRetries);
131
272
  this.retryBaseDelayMs = config.retryBaseDelayMs ?? DEFAULT_RETRY_BASE_DELAY_MS;
273
+ this.signingKey = config.signingKey === void 0 ? void 0 : loadSigningKey(config.signingKey);
132
274
  }
133
275
  // ── Health + Version (anonymous) ──────────────────────────────
134
276
  async health() {
@@ -137,20 +279,52 @@ var VerdifaxClient = class {
137
279
  async version() {
138
280
  return this.request("GET", "/version");
139
281
  }
282
+ /** The server's own API description. */
283
+ async getOpenAPI() {
284
+ return this.request("GET", "/openapi.json");
285
+ }
286
+ /** Run counts for this API key. */
287
+ async getStats() {
288
+ this.requireAPIKey();
289
+ return this.request("GET", "/stats");
290
+ }
140
291
  // ── Execute / Attest (auth) ───────────────────────────────────
141
292
  async execute(req) {
293
+ this.requireExecuteFields(req);
294
+ return this.request("POST", "/execute", this.prepared(req));
295
+ }
296
+ /** Runs one payload twice on the server and compares the manifest
297
+ * hashes. One signature covers the request, and the server claims
298
+ * its nonce once. Both runs are recorded. */
299
+ async verifyDeterminism(req) {
300
+ this.requireExecuteFields(req);
301
+ return this.request(
302
+ "POST",
303
+ "/execute/verify-determinism",
304
+ this.prepared(req)
305
+ );
306
+ }
307
+ /** Sends up to {@link MAX_BATCH_ROWS} runs in one request. With a
308
+ * signing key each row carries its own signature and nonce. A row
309
+ * that fails comes back in `results` with `ok: false`, so the call
310
+ * does not throw for it. The rows are not checked here: the server
311
+ * reports a bad row in that row's result.
312
+ *
313
+ * This is one POST and it is never retried. After a timeout the
314
+ * server may have run some or all of the rows. */
315
+ async executeBatch(runs) {
142
316
  this.requireAPIKey();
143
- if (!req.payload && !req.payload_text) {
144
- throw new VerdifaxError(
145
- "verdifax: ExecuteRequest needs payload or payload_text"
146
- );
317
+ if (!Array.isArray(runs) || runs.length === 0) {
318
+ throw new VerdifaxError("verdifax: executeBatch needs at least one run");
147
319
  }
148
- if (!req.program_id || !req.route_id || !req.registry_record_hash) {
320
+ if (runs.length > MAX_BATCH_ROWS) {
149
321
  throw new VerdifaxError(
150
- "verdifax: program_id, route_id, and registry_record_hash are required"
322
+ `verdifax: executeBatch accepts at most ${MAX_BATCH_ROWS} runs (got ${runs.length})`
151
323
  );
152
324
  }
153
- return this.request("POST", "/execute", req);
325
+ return this.request("POST", "/execute/batch", {
326
+ runs: runs.map((run) => this.prepared(run))
327
+ });
154
328
  }
155
329
  /** High-level convenience wrapper around `execute()`. Accepts
156
330
  * either a Buffer (for binary payloads, auto-base64) or a
@@ -176,7 +350,12 @@ var VerdifaxClient = class {
176
350
  route_id: req.route_id,
177
351
  registry_record_hash: req.registry_record_hash,
178
352
  ai_output_text: req.ai_output_text,
179
- attested_context: req.attested_context
353
+ attested_context: req.attested_context,
354
+ reproducibility_context: req.reproducibility_context,
355
+ request_substance: req.request_substance,
356
+ authorization_chain: req.authorization_chain,
357
+ regulatory_scaffolding: req.regulatory_scaffolding,
358
+ causal_graph: req.causal_graph
180
359
  };
181
360
  if (req.payload !== void 0) {
182
361
  body.payload = Buffer.isBuffer(req.payload) ? req.payload.toString("base64") : Buffer.from(req.payload).toString("base64");
@@ -185,6 +364,29 @@ var VerdifaxClient = class {
185
364
  }
186
365
  return this.execute(body);
187
366
  }
367
+ // ── Request signing (auth) ───────────────────────────────────
368
+ /** Registers this client's public key on its API key, with the proof
369
+ * that the client holds the private key. From then on the server
370
+ * refuses a run on this API key without a request signature. */
371
+ async registerRequestKey() {
372
+ this.requireAPIKey();
373
+ if (!this.signingKey) {
374
+ throw new VerdifaxError(
375
+ "verdifax: registerRequestKey needs the signingKey client option"
376
+ );
377
+ }
378
+ return this.request(
379
+ "POST",
380
+ REQUEST_KEY_PATH,
381
+ requestKeyRegistrationBody(this.signingKey)
382
+ );
383
+ }
384
+ /** Clears the registered public key, so the server stops requiring
385
+ * request signatures on this API key. */
386
+ async clearRequestKey() {
387
+ this.requireAPIKey();
388
+ await this.request("DELETE", REQUEST_KEY_PATH);
389
+ }
188
390
  // ── Runs ──────────────────────────────────────────────────────
189
391
  async getRun(runId) {
190
392
  this.requireAPIKey();
@@ -210,6 +412,94 @@ var VerdifaxClient = class {
210
412
  const out = await this.request("GET", path);
211
413
  return { runs: out.runs ?? [], total: out.total ?? 0 };
212
414
  }
415
+ /** Searches this key's runs. `manifest_hash` matches a prefix. The
416
+ * server ignores a `from` or `to` it cannot parse here, unlike
417
+ * {@link exportRuns}, which refuses one. */
418
+ async searchRuns(params = {}) {
419
+ this.requireAPIKey();
420
+ const q = new URLSearchParams();
421
+ for (const name of [
422
+ "manifest_hash",
423
+ "program_id",
424
+ "route_id",
425
+ "status",
426
+ "from",
427
+ "to"
428
+ ]) {
429
+ const value = params[name];
430
+ if (value) q.set(name, value);
431
+ }
432
+ if (params.limit && params.limit > 0) {
433
+ q.set("limit", String(params.limit));
434
+ }
435
+ if (params.offset && params.offset > 0) {
436
+ q.set("offset", String(params.offset));
437
+ }
438
+ const qs = q.toString();
439
+ const path = qs ? `/runs/search?${qs}` : "/runs/search";
440
+ const out = await this.request("GET", path);
441
+ return { runs: out.runs ?? [], total: out.total ?? 0 };
442
+ }
443
+ /** Lists what the server holds for this key in a period, failures
444
+ * included, up to 10,000 rows. `truncated` is true when the period
445
+ * holds more. The list states what was stored, not a verdict, and a
446
+ * call that never reached Verdifax cannot appear in it. */
447
+ async exportRuns(params = {}) {
448
+ this.requireAPIKey();
449
+ return this.request("GET", this.exportPath(params, "json"));
450
+ }
451
+ /** The same rows as {@link exportRuns}: the raw CSV text with `total`
452
+ * and `truncated` from the response headers. Check `truncated`: when
453
+ * true, the CSV is missing rows for the period. Throws when the server
454
+ * does not send those headers, so that a cut-off export cannot pass
455
+ * for a complete one. */
456
+ async exportRunsCSV(params = {}) {
457
+ this.requireAPIKey();
458
+ const { text, headers } = await this.requestRaw(
459
+ this.exportPath(params, "csv")
460
+ );
461
+ const total = Number.parseInt(headers.get(HEADER_EXPORT_TOTAL) ?? "", 10);
462
+ const truncated = headers.get(HEADER_EXPORT_TRUNCATED);
463
+ if (!Number.isSafeInteger(total) || truncated !== "true" && truncated !== "false") {
464
+ throw new VerdifaxError(
465
+ `verdifax: the server did not report ${HEADER_EXPORT_TOTAL} and ${HEADER_EXPORT_TRUNCATED}, so this CSV may be cut off; use exportRuns instead`
466
+ );
467
+ }
468
+ return { csv: text, total, truncated: truncated === "true" };
469
+ }
470
+ exportPath(params, format) {
471
+ const q = new URLSearchParams();
472
+ if (params.from) q.set("from", params.from);
473
+ if (params.to) q.set("to", params.to);
474
+ q.set("format", format);
475
+ return `/runs/export?${q.toString()}`;
476
+ }
477
+ // ── Attestation receipts (auth) ──────────────────────────────
478
+ async listAttestations(params = {}) {
479
+ this.requireAPIKey();
480
+ const q = new URLSearchParams();
481
+ if (params.limit && params.limit > 0) {
482
+ q.set("limit", String(params.limit));
483
+ }
484
+ if (params.offset && params.offset > 0) {
485
+ q.set("offset", String(params.offset));
486
+ }
487
+ const qs = q.toString();
488
+ return this.request(
489
+ "GET",
490
+ qs ? `/attestations?${qs}` : "/attestations"
491
+ );
492
+ }
493
+ async getAttestation(attestationId) {
494
+ this.requireAPIKey();
495
+ if (attestationId <= 0) {
496
+ throw new VerdifaxError("verdifax: attestationId must be > 0");
497
+ }
498
+ return this.request(
499
+ "GET",
500
+ `/attestations/${attestationId}`
501
+ );
502
+ }
213
503
  // ── Sealed-artifact retrieval (auth) ─────────────────────────
214
504
  async getAllowToken(runId) {
215
505
  return this.getRunArtifact(runId, "allow-token");
@@ -226,6 +516,39 @@ var VerdifaxClient = class {
226
516
  async getAivpT4HaltReceipt(runId) {
227
517
  return this.getRunArtifact(runId, "aivp-t4-halt-receipt");
228
518
  }
519
+ async getAttestationGapReceipt(runId) {
520
+ return this.getRunArtifact(runId, "attestation-gap-receipt");
521
+ }
522
+ async getCESAuditRecord(runId) {
523
+ return this.getRunArtifact(runId, "ces-audit-record");
524
+ }
525
+ /** The audit report for a run, as JSON. */
526
+ async getRunReport(runId) {
527
+ const out = await this.getRunArtifact(runId, "report");
528
+ return out.report;
529
+ }
530
+ /** The run's sealed audit bundle as the exact JSON text the server
531
+ * sent, to save and check with `verdifax-verify`. It is not parsed:
532
+ * JSON.parse would round a number above 2^53 and the recomputed hash
533
+ * would stop matching. */
534
+ async getRunArtifacts(runId) {
535
+ this.requireAPIKey();
536
+ if (runId <= 0) throw new VerdifaxError("verdifax: runId must be > 0");
537
+ return (await this.requestRaw(`/runs/${runId}/artifacts`)).text;
538
+ }
539
+ /** The public audit bundle of a record published by run id, as the
540
+ * exact JSON text the server sent. Needs no API key. */
541
+ async getPublicBundle(runId) {
542
+ if (runId <= 0) throw new VerdifaxError("verdifax: runId must be > 0");
543
+ return (await this.requestRaw(`/runs/${runId}/bundle.json`)).text;
544
+ }
545
+ /** The public audit bundle a share token points to, as the exact JSON
546
+ * text the server sent. Needs no API key. */
547
+ async getPublicBundleByToken(token) {
548
+ if (!token) throw new VerdifaxError("verdifax: token is required");
549
+ const path = `/public/runs/${encodeURIComponent(token)}/bundle.json`;
550
+ return (await this.requestRaw(path)).text;
551
+ }
229
552
  async getRunArtifact(runId, suffix) {
230
553
  this.requireAPIKey();
231
554
  if (runId <= 0) throw new VerdifaxError("verdifax: runId must be > 0");
@@ -312,13 +635,15 @@ var VerdifaxClient = class {
312
635
  await this.request("DELETE", `/admin/keys/${keyId}`);
313
636
  }
314
637
  // ── Provider helpers (Claude / OpenAI) ───────────────────────
315
- /** Convenience for attesting an Anthropic Claude API response.
316
- * Wires the response text into the AIVP governance pipeline
317
- * by setting `ai_output_text`. */
638
+ /** Convenience for attesting an Anthropic Claude API response. With
639
+ * `govern: true` the response text also goes to the AIVP review as
640
+ * `ai_output_text`; that is off by default, so the text leaves your
641
+ * machine for review only when you ask. */
318
642
  async attestClaudeResponse(args) {
319
643
  return this.attestProviderResponse({ ...args, provider: "anthropic" });
320
644
  }
321
- /** Convenience for attesting an OpenAI API response. */
645
+ /** Convenience for attesting an OpenAI API response. `govern` works as
646
+ * in {@link attestClaudeResponse}. */
322
647
  async attestOpenAIResponse(args) {
323
648
  return this.attestProviderResponse({ ...args, provider: "openai" });
324
649
  }
@@ -328,17 +653,16 @@ var VerdifaxClient = class {
328
653
  "verdifax: response is required for attestation"
329
654
  );
330
655
  }
331
- const payloadText = JSON.stringify({
332
- provider: args.provider,
333
- prompt: args.prompt,
334
- response: args.response
335
- });
336
656
  return this.attest({
337
- payload_text: payloadText,
657
+ payload_text: formatProviderPayload(
658
+ args.provider,
659
+ args.prompt,
660
+ args.response
661
+ ),
338
662
  program_id: args.program_id,
339
663
  route_id: args.route_id,
340
664
  registry_record_hash: args.registry_record_hash,
341
- ai_output_text: args.response,
665
+ ai_output_text: args.govern ? args.response : void 0,
342
666
  attested_context: {
343
667
  actor_role: "ai_assistant",
344
668
  model_provider: args.provider
@@ -346,7 +670,7 @@ var VerdifaxClient = class {
346
670
  });
347
671
  }
348
672
  // ── HTTP plumbing, retry-aware ──────────────────────────────
349
- async request(method, path, body) {
673
+ async request(method, path, body, rawText = false) {
350
674
  const url = this.baseURL + path;
351
675
  const headers = {
352
676
  "User-Agent": this.userAgent
@@ -403,6 +727,7 @@ var VerdifaxClient = class {
403
727
  throw new APIError(resp.status, path, errMessage);
404
728
  }
405
729
  }
730
+ if (rawText) return { text, headers: resp.headers };
406
731
  if (text.length === 0) return void 0;
407
732
  try {
408
733
  return JSON.parse(text);
@@ -416,17 +741,44 @@ var VerdifaxClient = class {
416
741
  `verdifax: retry budget exhausted${lastErr ? `: ${String(lastErr)}` : ""}`
417
742
  );
418
743
  }
744
+ /** A GET whose body is kept as sent, retried like any other GET. */
745
+ requestRaw(path) {
746
+ return this.request("GET", path, void 0, true);
747
+ }
419
748
  requireAPIKey() {
420
749
  if (!this.apiKey) throw new APIKeyRequiredError();
421
750
  }
751
+ requireExecuteFields(req) {
752
+ this.requireAPIKey();
753
+ if (!req.payload && !req.payload_text) {
754
+ throw new VerdifaxError(
755
+ "verdifax: ExecuteRequest needs payload or payload_text"
756
+ );
757
+ }
758
+ if (!req.program_id || !req.route_id || !req.registry_record_hash) {
759
+ throw new VerdifaxError(
760
+ "verdifax: program_id, route_id, and registry_record_hash are required"
761
+ );
762
+ }
763
+ validateHex64(req.program_id, "program_id");
764
+ validateHex64(req.registry_record_hash, "registry_record_hash");
765
+ validateRouteId(req.route_id);
766
+ refuseCallerOriginHashes(req.request_substance);
767
+ }
768
+ /** The request with its derived flags and, with a signing key, a fresh
769
+ * signature and nonce, on a copy. */
770
+ prepared(req) {
771
+ const body = withExecuteFlags(req);
772
+ return this.signingKey ? { ...body, request_signature: signRequest(this.signingKey, body) } : body;
773
+ }
422
774
  };
423
775
 
424
776
  // src/config.ts
425
777
  function fromEnv() {
426
778
  const cfg = {
427
- baseURL: process.env.VERDIFAX_BASE_URL ?? void 0,
779
+ baseURL: process.env.VERDIFAX_BASE_URL || process.env.VERDIFAX_API_URL || void 0,
428
780
  apiKey: process.env.VERDIFAX_API_KEY ?? void 0,
429
- userAgent: process.env.VERDIFAX_USER_AGENT ?? void 0
781
+ userAgent: process.env.VERDIFAX_USER_AGENT || void 0
430
782
  };
431
783
  const timeoutRaw = process.env.VERDIFAX_TIMEOUT_MS;
432
784
  if (timeoutRaw !== void 0 && timeoutRaw !== "") {
@@ -450,15 +802,182 @@ function fromEnv() {
450
802
  }
451
803
  return new VerdifaxClient(cfg);
452
804
  }
805
+
806
+ // src/environment.ts
807
+ var import_node_child_process = require("child_process");
808
+ var import_node_fs = require("fs");
809
+ var import_node_path = require("path");
810
+ var HASH_64 = /^[0-9a-f]{64}$/;
811
+ var GIT_SHA = /^([0-9a-f]{40}|[0-9a-f]{64})$/;
812
+ var PLATFORM_NAMES = { win32: "windows" };
813
+ var ARCH_NAMES = { x64: "amd64", ia32: "386" };
814
+ function packageVersion(dir) {
815
+ try {
816
+ const pkg = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.join)(dir, "package.json"), "utf8"));
817
+ return pkg.name && pkg.version ? `${pkg.name}==${pkg.version}` : void 0;
818
+ } catch {
819
+ return void 0;
820
+ }
821
+ }
822
+ function packageDirs(root) {
823
+ const dirs = [];
824
+ for (const entry of (0, import_node_fs.readdirSync)(root)) {
825
+ if (entry.startsWith(".")) continue;
826
+ if (!entry.startsWith("@")) {
827
+ dirs.push((0, import_node_path.join)(root, entry));
828
+ continue;
829
+ }
830
+ for (const scoped of (0, import_node_fs.readdirSync)((0, import_node_path.join)(root, entry))) {
831
+ dirs.push((0, import_node_path.join)(root, entry, scoped));
832
+ }
833
+ }
834
+ return dirs;
835
+ }
836
+ function detectDependencies() {
837
+ try {
838
+ const pins = packageDirs((0, import_node_path.join)(process.cwd(), "node_modules")).map(packageVersion).filter((pin) => pin !== void 0);
839
+ pins.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
840
+ return pins.length > 0 ? pins : void 0;
841
+ } catch {
842
+ return void 0;
843
+ }
844
+ }
845
+ function detectGitSHA() {
846
+ try {
847
+ const sha = (0, import_node_child_process.execFileSync)("git", ["rev-parse", "HEAD"], {
848
+ encoding: "utf8",
849
+ stdio: ["ignore", "pipe", "ignore"],
850
+ timeout: 2e3
851
+ }).trim().toLowerCase();
852
+ return GIT_SHA.test(sha) ? sha : void 0;
853
+ } catch {
854
+ return void 0;
855
+ }
856
+ }
857
+ function detectContainerHash() {
858
+ try {
859
+ const text = (0, import_node_fs.readFileSync)("/proc/self/cgroup", "utf8");
860
+ return text.split("\n").flatMap((line) => line.split("/")).map((segment) => segment.trim()).find((segment) => HASH_64.test(segment));
861
+ } catch {
862
+ return void 0;
863
+ }
864
+ }
865
+ function captureEnvironment(options = {}) {
866
+ const seeds = Object.entries(options.declaredSeeds ?? {}).map(([name, seed]) => `${name}=${seed}`).sort();
867
+ const pinned = options.includeDependencies === false ? void 0 : detectDependencies();
868
+ const git = options.includeGit === false ? void 0 : detectGitSHA();
869
+ const container = options.includeContainer === false ? void 0 : detectContainerHash();
870
+ const os = PLATFORM_NAMES[process.platform] ?? process.platform;
871
+ const arch = ARCH_NAMES[process.arch] ?? process.arch;
872
+ return {
873
+ declared: true,
874
+ runtime_name: "node",
875
+ runtime_version: process.versions.node,
876
+ platform: `${os}/${arch}`,
877
+ ...pinned ? { pinned_dependencies: pinned } : {},
878
+ ...git ? { git_commit_sha: git } : {},
879
+ ...container ? { container_image_hash: container } : {},
880
+ ...seeds.length > 0 ? { random_seeds: seeds } : {}
881
+ };
882
+ }
883
+
884
+ // src/types.ts
885
+ var RUN_STATUSES = [
886
+ "ok",
887
+ "probe",
888
+ "pepg_deny",
889
+ "ccv_halt",
890
+ "macc_halt",
891
+ "aivp_t4_halt",
892
+ "attestation_gap",
893
+ "stage_error"
894
+ ];
895
+
896
+ // src/section0.ts
897
+ var import_node_crypto2 = require("crypto");
898
+ var DSE_FORMULA_VERSION = "dse.tcu.dispatch.v1";
899
+ var DCAE_PREIMAGE_VERSION = "verdifax.dcae.v1";
900
+ var CRES_PREIMAGE_VERSION = "verdifax.cres.v1";
901
+ var CRES_ENGINE_VERSION = "verdifax-cres/1.0.0";
902
+ function sha256Hex(preimage) {
903
+ return (0, import_node_crypto2.createHash)("sha256").update(Buffer.from(preimage, "utf8")).digest("hex");
904
+ }
905
+ function requireHex64(value, name) {
906
+ try {
907
+ validateHex64(value, name);
908
+ } catch (err) {
909
+ throw new VerdifaxError(
910
+ `verdifax: section0: ${err instanceof Error ? err.message : String(err)}`
911
+ );
912
+ }
913
+ }
914
+ function dseDispatchHash(fields) {
915
+ requireHex64(fields.program_id, "program_id");
916
+ requireHex64(fields.registry_record_hash, "registry_record_hash");
917
+ return sha256Hex(
918
+ [
919
+ DSE_FORMULA_VERSION,
920
+ fields.envelope_id,
921
+ fields.sequence_id,
922
+ fields.program_id,
923
+ fields.registry_record_hash
924
+ ].join(".")
925
+ );
926
+ }
927
+ function dcaeClosureHash(fields) {
928
+ requireHex64(fields.manifest_hash, "manifest_hash");
929
+ requireHex64(fields.aer_hash, "aer_hash");
930
+ requireHex64(fields.zksp_binding_hash, "zksp_binding_hash");
931
+ return sha256Hex(
932
+ [
933
+ DCAE_PREIMAGE_VERSION,
934
+ fields.manifest_hash,
935
+ fields.aer_hash,
936
+ fields.envelope_id,
937
+ fields.zksp_binding_hash,
938
+ fields.formal_verifier_status
939
+ ].join("|")
940
+ );
941
+ }
942
+ function cresReceiptHash(fields) {
943
+ requireHex64(fields.ciphertext_hash_at_shred, "ciphertext_hash_at_shred");
944
+ return sha256Hex(
945
+ [
946
+ CRES_PREIMAGE_VERSION,
947
+ fields.envelope_id,
948
+ fields.field_path,
949
+ fields.deletion_clock,
950
+ fields.actor_id,
951
+ fields.ciphertext_hash_at_shred,
952
+ fields.dsar_reference,
953
+ fields.engine_version ?? CRES_ENGINE_VERSION
954
+ ].join("|")
955
+ );
956
+ }
453
957
  // Annotate the CommonJS export names for ESM import in node:
454
958
  0 && (module.exports = {
455
959
  APIError,
456
960
  APIKeyRequiredError,
457
961
  AuthError,
962
+ CRES_ENGINE_VERSION,
963
+ CRES_PREIMAGE_VERSION,
964
+ DCAE_PREIMAGE_VERSION,
965
+ DSE_FORMULA_VERSION,
966
+ MAX_BATCH_ROWS,
458
967
  NotFoundError,
968
+ RUN_STATUSES,
459
969
  VerdifaxClient,
460
970
  VerdifaxError,
971
+ captureEnvironment,
972
+ cresReceiptHash,
973
+ dcaeClosureHash,
974
+ dseDispatchHash,
975
+ formatProviderPayload,
461
976
  fromEnv,
977
+ generateRequestKeyPair,
978
+ requestKeyRegistrationBody,
979
+ requestSignaturePreimage,
980
+ signRequest,
462
981
  validateHex64,
463
982
  validateRouteId
464
983
  });