verdifax 0.3.0 → 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/README.md +123 -6
- package/dist/index.d.mts +357 -10
- package/dist/index.d.ts +357 -10
- package/dist/index.js +430 -24
- package/dist/index.mjs +419 -24
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23,9 +23,20 @@ __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,
|
|
30
41
|
generateRequestKeyPair: () => generateRequestKeyPair,
|
|
31
42
|
requestKeyRegistrationBody: () => requestKeyRegistrationBody,
|
|
@@ -157,6 +168,49 @@ function requestKeyRegistrationBody(signingKey) {
|
|
|
157
168
|
return { public_key, proof };
|
|
158
169
|
}
|
|
159
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
|
+
|
|
160
214
|
// src/validation.ts
|
|
161
215
|
function validateHex64(value, fieldName) {
|
|
162
216
|
if (typeof value !== "string" || value.length !== 64) {
|
|
@@ -187,7 +241,7 @@ function validateRouteId(value) {
|
|
|
187
241
|
}
|
|
188
242
|
|
|
189
243
|
// src/version.ts
|
|
190
|
-
var SDK_VERSION = "0.
|
|
244
|
+
var SDK_VERSION = "1.0.0";
|
|
191
245
|
|
|
192
246
|
// src/client.ts
|
|
193
247
|
var DEFAULT_BASE_URL = "https://api.verdifax.com";
|
|
@@ -197,7 +251,10 @@ var DEFAULT_RETRY_BASE_DELAY_MS = 200;
|
|
|
197
251
|
var SDK_USER_AGENT = `verdifax-sdk-node/${SDK_VERSION}`;
|
|
198
252
|
var HEADER_API_KEY = "X-Verdifax-Key";
|
|
199
253
|
var REQUEST_KEY_PATH = "/keys/request-key";
|
|
254
|
+
var HEADER_EXPORT_TOTAL = "X-Verdifax-Total";
|
|
255
|
+
var HEADER_EXPORT_TRUNCATED = "X-Verdifax-Truncated";
|
|
200
256
|
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
|
|
257
|
+
var MAX_BATCH_ROWS = 1e3;
|
|
201
258
|
var VerdifaxClient = class {
|
|
202
259
|
baseURL;
|
|
203
260
|
apiKey;
|
|
@@ -222,21 +279,52 @@ var VerdifaxClient = class {
|
|
|
222
279
|
async version() {
|
|
223
280
|
return this.request("GET", "/version");
|
|
224
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
|
+
}
|
|
225
291
|
// ── Execute / Attest (auth) ───────────────────────────────────
|
|
226
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) {
|
|
227
316
|
this.requireAPIKey();
|
|
228
|
-
if (!
|
|
229
|
-
throw new VerdifaxError(
|
|
230
|
-
"verdifax: ExecuteRequest needs payload or payload_text"
|
|
231
|
-
);
|
|
317
|
+
if (!Array.isArray(runs) || runs.length === 0) {
|
|
318
|
+
throw new VerdifaxError("verdifax: executeBatch needs at least one run");
|
|
232
319
|
}
|
|
233
|
-
if (
|
|
320
|
+
if (runs.length > MAX_BATCH_ROWS) {
|
|
234
321
|
throw new VerdifaxError(
|
|
235
|
-
|
|
322
|
+
`verdifax: executeBatch accepts at most ${MAX_BATCH_ROWS} runs (got ${runs.length})`
|
|
236
323
|
);
|
|
237
324
|
}
|
|
238
|
-
|
|
239
|
-
|
|
325
|
+
return this.request("POST", "/execute/batch", {
|
|
326
|
+
runs: runs.map((run) => this.prepared(run))
|
|
327
|
+
});
|
|
240
328
|
}
|
|
241
329
|
/** High-level convenience wrapper around `execute()`. Accepts
|
|
242
330
|
* either a Buffer (for binary payloads, auto-base64) or a
|
|
@@ -262,7 +350,12 @@ var VerdifaxClient = class {
|
|
|
262
350
|
route_id: req.route_id,
|
|
263
351
|
registry_record_hash: req.registry_record_hash,
|
|
264
352
|
ai_output_text: req.ai_output_text,
|
|
265
|
-
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
|
|
266
359
|
};
|
|
267
360
|
if (req.payload !== void 0) {
|
|
268
361
|
body.payload = Buffer.isBuffer(req.payload) ? req.payload.toString("base64") : Buffer.from(req.payload).toString("base64");
|
|
@@ -319,6 +412,94 @@ var VerdifaxClient = class {
|
|
|
319
412
|
const out = await this.request("GET", path);
|
|
320
413
|
return { runs: out.runs ?? [], total: out.total ?? 0 };
|
|
321
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
|
+
}
|
|
322
503
|
// ── Sealed-artifact retrieval (auth) ─────────────────────────
|
|
323
504
|
async getAllowToken(runId) {
|
|
324
505
|
return this.getRunArtifact(runId, "allow-token");
|
|
@@ -335,6 +516,39 @@ var VerdifaxClient = class {
|
|
|
335
516
|
async getAivpT4HaltReceipt(runId) {
|
|
336
517
|
return this.getRunArtifact(runId, "aivp-t4-halt-receipt");
|
|
337
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
|
+
}
|
|
338
552
|
async getRunArtifact(runId, suffix) {
|
|
339
553
|
this.requireAPIKey();
|
|
340
554
|
if (runId <= 0) throw new VerdifaxError("verdifax: runId must be > 0");
|
|
@@ -421,13 +635,15 @@ var VerdifaxClient = class {
|
|
|
421
635
|
await this.request("DELETE", `/admin/keys/${keyId}`);
|
|
422
636
|
}
|
|
423
637
|
// ── Provider helpers (Claude / OpenAI) ───────────────────────
|
|
424
|
-
/** Convenience for attesting an Anthropic Claude API response.
|
|
425
|
-
*
|
|
426
|
-
* by
|
|
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. */
|
|
427
642
|
async attestClaudeResponse(args) {
|
|
428
643
|
return this.attestProviderResponse({ ...args, provider: "anthropic" });
|
|
429
644
|
}
|
|
430
|
-
/** Convenience for attesting an OpenAI API response.
|
|
645
|
+
/** Convenience for attesting an OpenAI API response. `govern` works as
|
|
646
|
+
* in {@link attestClaudeResponse}. */
|
|
431
647
|
async attestOpenAIResponse(args) {
|
|
432
648
|
return this.attestProviderResponse({ ...args, provider: "openai" });
|
|
433
649
|
}
|
|
@@ -437,17 +653,16 @@ var VerdifaxClient = class {
|
|
|
437
653
|
"verdifax: response is required for attestation"
|
|
438
654
|
);
|
|
439
655
|
}
|
|
440
|
-
const payloadText = JSON.stringify({
|
|
441
|
-
provider: args.provider,
|
|
442
|
-
prompt: args.prompt,
|
|
443
|
-
response: args.response
|
|
444
|
-
});
|
|
445
656
|
return this.attest({
|
|
446
|
-
payload_text:
|
|
657
|
+
payload_text: formatProviderPayload(
|
|
658
|
+
args.provider,
|
|
659
|
+
args.prompt,
|
|
660
|
+
args.response
|
|
661
|
+
),
|
|
447
662
|
program_id: args.program_id,
|
|
448
663
|
route_id: args.route_id,
|
|
449
664
|
registry_record_hash: args.registry_record_hash,
|
|
450
|
-
ai_output_text: args.response,
|
|
665
|
+
ai_output_text: args.govern ? args.response : void 0,
|
|
451
666
|
attested_context: {
|
|
452
667
|
actor_role: "ai_assistant",
|
|
453
668
|
model_provider: args.provider
|
|
@@ -455,7 +670,7 @@ var VerdifaxClient = class {
|
|
|
455
670
|
});
|
|
456
671
|
}
|
|
457
672
|
// ── HTTP plumbing, retry-aware ──────────────────────────────
|
|
458
|
-
async request(method, path, body) {
|
|
673
|
+
async request(method, path, body, rawText = false) {
|
|
459
674
|
const url = this.baseURL + path;
|
|
460
675
|
const headers = {
|
|
461
676
|
"User-Agent": this.userAgent
|
|
@@ -512,6 +727,7 @@ var VerdifaxClient = class {
|
|
|
512
727
|
throw new APIError(resp.status, path, errMessage);
|
|
513
728
|
}
|
|
514
729
|
}
|
|
730
|
+
if (rawText) return { text, headers: resp.headers };
|
|
515
731
|
if (text.length === 0) return void 0;
|
|
516
732
|
try {
|
|
517
733
|
return JSON.parse(text);
|
|
@@ -525,17 +741,44 @@ var VerdifaxClient = class {
|
|
|
525
741
|
`verdifax: retry budget exhausted${lastErr ? `: ${String(lastErr)}` : ""}`
|
|
526
742
|
);
|
|
527
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
|
+
}
|
|
528
748
|
requireAPIKey() {
|
|
529
749
|
if (!this.apiKey) throw new APIKeyRequiredError();
|
|
530
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
|
+
}
|
|
531
774
|
};
|
|
532
775
|
|
|
533
776
|
// src/config.ts
|
|
534
777
|
function fromEnv() {
|
|
535
778
|
const cfg = {
|
|
536
|
-
baseURL: process.env.VERDIFAX_BASE_URL
|
|
779
|
+
baseURL: process.env.VERDIFAX_BASE_URL || process.env.VERDIFAX_API_URL || void 0,
|
|
537
780
|
apiKey: process.env.VERDIFAX_API_KEY ?? void 0,
|
|
538
|
-
userAgent: process.env.VERDIFAX_USER_AGENT
|
|
781
|
+
userAgent: process.env.VERDIFAX_USER_AGENT || void 0
|
|
539
782
|
};
|
|
540
783
|
const timeoutRaw = process.env.VERDIFAX_TIMEOUT_MS;
|
|
541
784
|
if (timeoutRaw !== void 0 && timeoutRaw !== "") {
|
|
@@ -559,14 +802,177 @@ function fromEnv() {
|
|
|
559
802
|
}
|
|
560
803
|
return new VerdifaxClient(cfg);
|
|
561
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
|
+
}
|
|
562
957
|
// Annotate the CommonJS export names for ESM import in node:
|
|
563
958
|
0 && (module.exports = {
|
|
564
959
|
APIError,
|
|
565
960
|
APIKeyRequiredError,
|
|
566
961
|
AuthError,
|
|
962
|
+
CRES_ENGINE_VERSION,
|
|
963
|
+
CRES_PREIMAGE_VERSION,
|
|
964
|
+
DCAE_PREIMAGE_VERSION,
|
|
965
|
+
DSE_FORMULA_VERSION,
|
|
966
|
+
MAX_BATCH_ROWS,
|
|
567
967
|
NotFoundError,
|
|
968
|
+
RUN_STATUSES,
|
|
568
969
|
VerdifaxClient,
|
|
569
970
|
VerdifaxError,
|
|
971
|
+
captureEnvironment,
|
|
972
|
+
cresReceiptHash,
|
|
973
|
+
dcaeClosureHash,
|
|
974
|
+
dseDispatchHash,
|
|
975
|
+
formatProviderPayload,
|
|
570
976
|
fromEnv,
|
|
571
977
|
generateRequestKeyPair,
|
|
572
978
|
requestKeyRegistrationBody,
|