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.mjs
CHANGED
|
@@ -127,6 +127,49 @@ function requestKeyRegistrationBody(signingKey) {
|
|
|
127
127
|
return { public_key, proof };
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
// src/helpers.ts
|
|
131
|
+
function formatProviderPayload(provider, prompt, response) {
|
|
132
|
+
if ([provider, prompt, response].some((text) => /[\uD800-\uDFFF]/u.test(text))) {
|
|
133
|
+
throw new VerdifaxError(
|
|
134
|
+
"verdifax: provider payload text cannot be encoded as UTF-8"
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return JSON.stringify({ prompt, provider, response });
|
|
138
|
+
}
|
|
139
|
+
function isProvided(value) {
|
|
140
|
+
return value !== null && value !== void 0 && value !== "" && value !== false;
|
|
141
|
+
}
|
|
142
|
+
function withAttestedFlag(context) {
|
|
143
|
+
const provided = Object.entries(context).some(
|
|
144
|
+
([name, value]) => name !== "attested" && isProvided(value)
|
|
145
|
+
);
|
|
146
|
+
return { ...context, attested: provided };
|
|
147
|
+
}
|
|
148
|
+
function withDeclaredFlag(context) {
|
|
149
|
+
const declared = Object.entries(context).some(
|
|
150
|
+
([name, value]) => name !== "declared" && isProvided(value) && !(Array.isArray(value) && value.length === 0)
|
|
151
|
+
);
|
|
152
|
+
return { ...context, declared };
|
|
153
|
+
}
|
|
154
|
+
function refuseCallerOriginHashes(substance) {
|
|
155
|
+
const origin = substance?.origin;
|
|
156
|
+
if (origin?.source_ip_hash || origin?.user_agent_hash) {
|
|
157
|
+
throw new VerdifaxError(
|
|
158
|
+
"verdifax: request_substance.origin.source_ip_hash and user_agent_hash are computed by the server and cannot be supplied"
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function withExecuteFlags(req) {
|
|
163
|
+
const body = { ...req };
|
|
164
|
+
if (req.attested_context) {
|
|
165
|
+
body.attested_context = withAttestedFlag(req.attested_context);
|
|
166
|
+
}
|
|
167
|
+
if (req.reproducibility_context) {
|
|
168
|
+
body.reproducibility_context = withDeclaredFlag(req.reproducibility_context);
|
|
169
|
+
}
|
|
170
|
+
return body;
|
|
171
|
+
}
|
|
172
|
+
|
|
130
173
|
// src/validation.ts
|
|
131
174
|
function validateHex64(value, fieldName) {
|
|
132
175
|
if (typeof value !== "string" || value.length !== 64) {
|
|
@@ -157,7 +200,7 @@ function validateRouteId(value) {
|
|
|
157
200
|
}
|
|
158
201
|
|
|
159
202
|
// src/version.ts
|
|
160
|
-
var SDK_VERSION = "0.
|
|
203
|
+
var SDK_VERSION = "1.0.0";
|
|
161
204
|
|
|
162
205
|
// src/client.ts
|
|
163
206
|
var DEFAULT_BASE_URL = "https://api.verdifax.com";
|
|
@@ -167,7 +210,10 @@ var DEFAULT_RETRY_BASE_DELAY_MS = 200;
|
|
|
167
210
|
var SDK_USER_AGENT = `verdifax-sdk-node/${SDK_VERSION}`;
|
|
168
211
|
var HEADER_API_KEY = "X-Verdifax-Key";
|
|
169
212
|
var REQUEST_KEY_PATH = "/keys/request-key";
|
|
213
|
+
var HEADER_EXPORT_TOTAL = "X-Verdifax-Total";
|
|
214
|
+
var HEADER_EXPORT_TRUNCATED = "X-Verdifax-Truncated";
|
|
170
215
|
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
|
|
216
|
+
var MAX_BATCH_ROWS = 1e3;
|
|
171
217
|
var VerdifaxClient = class {
|
|
172
218
|
baseURL;
|
|
173
219
|
apiKey;
|
|
@@ -192,21 +238,52 @@ var VerdifaxClient = class {
|
|
|
192
238
|
async version() {
|
|
193
239
|
return this.request("GET", "/version");
|
|
194
240
|
}
|
|
241
|
+
/** The server's own API description. */
|
|
242
|
+
async getOpenAPI() {
|
|
243
|
+
return this.request("GET", "/openapi.json");
|
|
244
|
+
}
|
|
245
|
+
/** Run counts for this API key. */
|
|
246
|
+
async getStats() {
|
|
247
|
+
this.requireAPIKey();
|
|
248
|
+
return this.request("GET", "/stats");
|
|
249
|
+
}
|
|
195
250
|
// ── Execute / Attest (auth) ───────────────────────────────────
|
|
196
251
|
async execute(req) {
|
|
252
|
+
this.requireExecuteFields(req);
|
|
253
|
+
return this.request("POST", "/execute", this.prepared(req));
|
|
254
|
+
}
|
|
255
|
+
/** Runs one payload twice on the server and compares the manifest
|
|
256
|
+
* hashes. One signature covers the request, and the server claims
|
|
257
|
+
* its nonce once. Both runs are recorded. */
|
|
258
|
+
async verifyDeterminism(req) {
|
|
259
|
+
this.requireExecuteFields(req);
|
|
260
|
+
return this.request(
|
|
261
|
+
"POST",
|
|
262
|
+
"/execute/verify-determinism",
|
|
263
|
+
this.prepared(req)
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
/** Sends up to {@link MAX_BATCH_ROWS} runs in one request. With a
|
|
267
|
+
* signing key each row carries its own signature and nonce. A row
|
|
268
|
+
* that fails comes back in `results` with `ok: false`, so the call
|
|
269
|
+
* does not throw for it. The rows are not checked here: the server
|
|
270
|
+
* reports a bad row in that row's result.
|
|
271
|
+
*
|
|
272
|
+
* This is one POST and it is never retried. After a timeout the
|
|
273
|
+
* server may have run some or all of the rows. */
|
|
274
|
+
async executeBatch(runs) {
|
|
197
275
|
this.requireAPIKey();
|
|
198
|
-
if (!
|
|
199
|
-
throw new VerdifaxError(
|
|
200
|
-
"verdifax: ExecuteRequest needs payload or payload_text"
|
|
201
|
-
);
|
|
276
|
+
if (!Array.isArray(runs) || runs.length === 0) {
|
|
277
|
+
throw new VerdifaxError("verdifax: executeBatch needs at least one run");
|
|
202
278
|
}
|
|
203
|
-
if (
|
|
279
|
+
if (runs.length > MAX_BATCH_ROWS) {
|
|
204
280
|
throw new VerdifaxError(
|
|
205
|
-
|
|
281
|
+
`verdifax: executeBatch accepts at most ${MAX_BATCH_ROWS} runs (got ${runs.length})`
|
|
206
282
|
);
|
|
207
283
|
}
|
|
208
|
-
|
|
209
|
-
|
|
284
|
+
return this.request("POST", "/execute/batch", {
|
|
285
|
+
runs: runs.map((run) => this.prepared(run))
|
|
286
|
+
});
|
|
210
287
|
}
|
|
211
288
|
/** High-level convenience wrapper around `execute()`. Accepts
|
|
212
289
|
* either a Buffer (for binary payloads, auto-base64) or a
|
|
@@ -232,7 +309,12 @@ var VerdifaxClient = class {
|
|
|
232
309
|
route_id: req.route_id,
|
|
233
310
|
registry_record_hash: req.registry_record_hash,
|
|
234
311
|
ai_output_text: req.ai_output_text,
|
|
235
|
-
attested_context: req.attested_context
|
|
312
|
+
attested_context: req.attested_context,
|
|
313
|
+
reproducibility_context: req.reproducibility_context,
|
|
314
|
+
request_substance: req.request_substance,
|
|
315
|
+
authorization_chain: req.authorization_chain,
|
|
316
|
+
regulatory_scaffolding: req.regulatory_scaffolding,
|
|
317
|
+
causal_graph: req.causal_graph
|
|
236
318
|
};
|
|
237
319
|
if (req.payload !== void 0) {
|
|
238
320
|
body.payload = Buffer.isBuffer(req.payload) ? req.payload.toString("base64") : Buffer.from(req.payload).toString("base64");
|
|
@@ -289,6 +371,94 @@ var VerdifaxClient = class {
|
|
|
289
371
|
const out = await this.request("GET", path);
|
|
290
372
|
return { runs: out.runs ?? [], total: out.total ?? 0 };
|
|
291
373
|
}
|
|
374
|
+
/** Searches this key's runs. `manifest_hash` matches a prefix. The
|
|
375
|
+
* server ignores a `from` or `to` it cannot parse here, unlike
|
|
376
|
+
* {@link exportRuns}, which refuses one. */
|
|
377
|
+
async searchRuns(params = {}) {
|
|
378
|
+
this.requireAPIKey();
|
|
379
|
+
const q = new URLSearchParams();
|
|
380
|
+
for (const name of [
|
|
381
|
+
"manifest_hash",
|
|
382
|
+
"program_id",
|
|
383
|
+
"route_id",
|
|
384
|
+
"status",
|
|
385
|
+
"from",
|
|
386
|
+
"to"
|
|
387
|
+
]) {
|
|
388
|
+
const value = params[name];
|
|
389
|
+
if (value) q.set(name, value);
|
|
390
|
+
}
|
|
391
|
+
if (params.limit && params.limit > 0) {
|
|
392
|
+
q.set("limit", String(params.limit));
|
|
393
|
+
}
|
|
394
|
+
if (params.offset && params.offset > 0) {
|
|
395
|
+
q.set("offset", String(params.offset));
|
|
396
|
+
}
|
|
397
|
+
const qs = q.toString();
|
|
398
|
+
const path = qs ? `/runs/search?${qs}` : "/runs/search";
|
|
399
|
+
const out = await this.request("GET", path);
|
|
400
|
+
return { runs: out.runs ?? [], total: out.total ?? 0 };
|
|
401
|
+
}
|
|
402
|
+
/** Lists what the server holds for this key in a period, failures
|
|
403
|
+
* included, up to 10,000 rows. `truncated` is true when the period
|
|
404
|
+
* holds more. The list states what was stored, not a verdict, and a
|
|
405
|
+
* call that never reached Verdifax cannot appear in it. */
|
|
406
|
+
async exportRuns(params = {}) {
|
|
407
|
+
this.requireAPIKey();
|
|
408
|
+
return this.request("GET", this.exportPath(params, "json"));
|
|
409
|
+
}
|
|
410
|
+
/** The same rows as {@link exportRuns}: the raw CSV text with `total`
|
|
411
|
+
* and `truncated` from the response headers. Check `truncated`: when
|
|
412
|
+
* true, the CSV is missing rows for the period. Throws when the server
|
|
413
|
+
* does not send those headers, so that a cut-off export cannot pass
|
|
414
|
+
* for a complete one. */
|
|
415
|
+
async exportRunsCSV(params = {}) {
|
|
416
|
+
this.requireAPIKey();
|
|
417
|
+
const { text, headers } = await this.requestRaw(
|
|
418
|
+
this.exportPath(params, "csv")
|
|
419
|
+
);
|
|
420
|
+
const total = Number.parseInt(headers.get(HEADER_EXPORT_TOTAL) ?? "", 10);
|
|
421
|
+
const truncated = headers.get(HEADER_EXPORT_TRUNCATED);
|
|
422
|
+
if (!Number.isSafeInteger(total) || truncated !== "true" && truncated !== "false") {
|
|
423
|
+
throw new VerdifaxError(
|
|
424
|
+
`verdifax: the server did not report ${HEADER_EXPORT_TOTAL} and ${HEADER_EXPORT_TRUNCATED}, so this CSV may be cut off; use exportRuns instead`
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
return { csv: text, total, truncated: truncated === "true" };
|
|
428
|
+
}
|
|
429
|
+
exportPath(params, format) {
|
|
430
|
+
const q = new URLSearchParams();
|
|
431
|
+
if (params.from) q.set("from", params.from);
|
|
432
|
+
if (params.to) q.set("to", params.to);
|
|
433
|
+
q.set("format", format);
|
|
434
|
+
return `/runs/export?${q.toString()}`;
|
|
435
|
+
}
|
|
436
|
+
// ── Attestation receipts (auth) ──────────────────────────────
|
|
437
|
+
async listAttestations(params = {}) {
|
|
438
|
+
this.requireAPIKey();
|
|
439
|
+
const q = new URLSearchParams();
|
|
440
|
+
if (params.limit && params.limit > 0) {
|
|
441
|
+
q.set("limit", String(params.limit));
|
|
442
|
+
}
|
|
443
|
+
if (params.offset && params.offset > 0) {
|
|
444
|
+
q.set("offset", String(params.offset));
|
|
445
|
+
}
|
|
446
|
+
const qs = q.toString();
|
|
447
|
+
return this.request(
|
|
448
|
+
"GET",
|
|
449
|
+
qs ? `/attestations?${qs}` : "/attestations"
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
async getAttestation(attestationId) {
|
|
453
|
+
this.requireAPIKey();
|
|
454
|
+
if (attestationId <= 0) {
|
|
455
|
+
throw new VerdifaxError("verdifax: attestationId must be > 0");
|
|
456
|
+
}
|
|
457
|
+
return this.request(
|
|
458
|
+
"GET",
|
|
459
|
+
`/attestations/${attestationId}`
|
|
460
|
+
);
|
|
461
|
+
}
|
|
292
462
|
// ── Sealed-artifact retrieval (auth) ─────────────────────────
|
|
293
463
|
async getAllowToken(runId) {
|
|
294
464
|
return this.getRunArtifact(runId, "allow-token");
|
|
@@ -305,6 +475,39 @@ var VerdifaxClient = class {
|
|
|
305
475
|
async getAivpT4HaltReceipt(runId) {
|
|
306
476
|
return this.getRunArtifact(runId, "aivp-t4-halt-receipt");
|
|
307
477
|
}
|
|
478
|
+
async getAttestationGapReceipt(runId) {
|
|
479
|
+
return this.getRunArtifact(runId, "attestation-gap-receipt");
|
|
480
|
+
}
|
|
481
|
+
async getCESAuditRecord(runId) {
|
|
482
|
+
return this.getRunArtifact(runId, "ces-audit-record");
|
|
483
|
+
}
|
|
484
|
+
/** The audit report for a run, as JSON. */
|
|
485
|
+
async getRunReport(runId) {
|
|
486
|
+
const out = await this.getRunArtifact(runId, "report");
|
|
487
|
+
return out.report;
|
|
488
|
+
}
|
|
489
|
+
/** The run's sealed audit bundle as the exact JSON text the server
|
|
490
|
+
* sent, to save and check with `verdifax-verify`. It is not parsed:
|
|
491
|
+
* JSON.parse would round a number above 2^53 and the recomputed hash
|
|
492
|
+
* would stop matching. */
|
|
493
|
+
async getRunArtifacts(runId) {
|
|
494
|
+
this.requireAPIKey();
|
|
495
|
+
if (runId <= 0) throw new VerdifaxError("verdifax: runId must be > 0");
|
|
496
|
+
return (await this.requestRaw(`/runs/${runId}/artifacts`)).text;
|
|
497
|
+
}
|
|
498
|
+
/** The public audit bundle of a record published by run id, as the
|
|
499
|
+
* exact JSON text the server sent. Needs no API key. */
|
|
500
|
+
async getPublicBundle(runId) {
|
|
501
|
+
if (runId <= 0) throw new VerdifaxError("verdifax: runId must be > 0");
|
|
502
|
+
return (await this.requestRaw(`/runs/${runId}/bundle.json`)).text;
|
|
503
|
+
}
|
|
504
|
+
/** The public audit bundle a share token points to, as the exact JSON
|
|
505
|
+
* text the server sent. Needs no API key. */
|
|
506
|
+
async getPublicBundleByToken(token) {
|
|
507
|
+
if (!token) throw new VerdifaxError("verdifax: token is required");
|
|
508
|
+
const path = `/public/runs/${encodeURIComponent(token)}/bundle.json`;
|
|
509
|
+
return (await this.requestRaw(path)).text;
|
|
510
|
+
}
|
|
308
511
|
async getRunArtifact(runId, suffix) {
|
|
309
512
|
this.requireAPIKey();
|
|
310
513
|
if (runId <= 0) throw new VerdifaxError("verdifax: runId must be > 0");
|
|
@@ -391,13 +594,15 @@ var VerdifaxClient = class {
|
|
|
391
594
|
await this.request("DELETE", `/admin/keys/${keyId}`);
|
|
392
595
|
}
|
|
393
596
|
// ── Provider helpers (Claude / OpenAI) ───────────────────────
|
|
394
|
-
/** Convenience for attesting an Anthropic Claude API response.
|
|
395
|
-
*
|
|
396
|
-
* by
|
|
597
|
+
/** Convenience for attesting an Anthropic Claude API response. With
|
|
598
|
+
* `govern: true` the response text also goes to the AIVP review as
|
|
599
|
+
* `ai_output_text`; that is off by default, so the text leaves your
|
|
600
|
+
* machine for review only when you ask. */
|
|
397
601
|
async attestClaudeResponse(args) {
|
|
398
602
|
return this.attestProviderResponse({ ...args, provider: "anthropic" });
|
|
399
603
|
}
|
|
400
|
-
/** Convenience for attesting an OpenAI API response.
|
|
604
|
+
/** Convenience for attesting an OpenAI API response. `govern` works as
|
|
605
|
+
* in {@link attestClaudeResponse}. */
|
|
401
606
|
async attestOpenAIResponse(args) {
|
|
402
607
|
return this.attestProviderResponse({ ...args, provider: "openai" });
|
|
403
608
|
}
|
|
@@ -407,17 +612,16 @@ var VerdifaxClient = class {
|
|
|
407
612
|
"verdifax: response is required for attestation"
|
|
408
613
|
);
|
|
409
614
|
}
|
|
410
|
-
const payloadText = JSON.stringify({
|
|
411
|
-
provider: args.provider,
|
|
412
|
-
prompt: args.prompt,
|
|
413
|
-
response: args.response
|
|
414
|
-
});
|
|
415
615
|
return this.attest({
|
|
416
|
-
payload_text:
|
|
616
|
+
payload_text: formatProviderPayload(
|
|
617
|
+
args.provider,
|
|
618
|
+
args.prompt,
|
|
619
|
+
args.response
|
|
620
|
+
),
|
|
417
621
|
program_id: args.program_id,
|
|
418
622
|
route_id: args.route_id,
|
|
419
623
|
registry_record_hash: args.registry_record_hash,
|
|
420
|
-
ai_output_text: args.response,
|
|
624
|
+
ai_output_text: args.govern ? args.response : void 0,
|
|
421
625
|
attested_context: {
|
|
422
626
|
actor_role: "ai_assistant",
|
|
423
627
|
model_provider: args.provider
|
|
@@ -425,7 +629,7 @@ var VerdifaxClient = class {
|
|
|
425
629
|
});
|
|
426
630
|
}
|
|
427
631
|
// ── HTTP plumbing, retry-aware ──────────────────────────────
|
|
428
|
-
async request(method, path, body) {
|
|
632
|
+
async request(method, path, body, rawText = false) {
|
|
429
633
|
const url = this.baseURL + path;
|
|
430
634
|
const headers = {
|
|
431
635
|
"User-Agent": this.userAgent
|
|
@@ -482,6 +686,7 @@ var VerdifaxClient = class {
|
|
|
482
686
|
throw new APIError(resp.status, path, errMessage);
|
|
483
687
|
}
|
|
484
688
|
}
|
|
689
|
+
if (rawText) return { text, headers: resp.headers };
|
|
485
690
|
if (text.length === 0) return void 0;
|
|
486
691
|
try {
|
|
487
692
|
return JSON.parse(text);
|
|
@@ -495,17 +700,44 @@ var VerdifaxClient = class {
|
|
|
495
700
|
`verdifax: retry budget exhausted${lastErr ? `: ${String(lastErr)}` : ""}`
|
|
496
701
|
);
|
|
497
702
|
}
|
|
703
|
+
/** A GET whose body is kept as sent, retried like any other GET. */
|
|
704
|
+
requestRaw(path) {
|
|
705
|
+
return this.request("GET", path, void 0, true);
|
|
706
|
+
}
|
|
498
707
|
requireAPIKey() {
|
|
499
708
|
if (!this.apiKey) throw new APIKeyRequiredError();
|
|
500
709
|
}
|
|
710
|
+
requireExecuteFields(req) {
|
|
711
|
+
this.requireAPIKey();
|
|
712
|
+
if (!req.payload && !req.payload_text) {
|
|
713
|
+
throw new VerdifaxError(
|
|
714
|
+
"verdifax: ExecuteRequest needs payload or payload_text"
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
if (!req.program_id || !req.route_id || !req.registry_record_hash) {
|
|
718
|
+
throw new VerdifaxError(
|
|
719
|
+
"verdifax: program_id, route_id, and registry_record_hash are required"
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
validateHex64(req.program_id, "program_id");
|
|
723
|
+
validateHex64(req.registry_record_hash, "registry_record_hash");
|
|
724
|
+
validateRouteId(req.route_id);
|
|
725
|
+
refuseCallerOriginHashes(req.request_substance);
|
|
726
|
+
}
|
|
727
|
+
/** The request with its derived flags and, with a signing key, a fresh
|
|
728
|
+
* signature and nonce, on a copy. */
|
|
729
|
+
prepared(req) {
|
|
730
|
+
const body = withExecuteFlags(req);
|
|
731
|
+
return this.signingKey ? { ...body, request_signature: signRequest(this.signingKey, body) } : body;
|
|
732
|
+
}
|
|
501
733
|
};
|
|
502
734
|
|
|
503
735
|
// src/config.ts
|
|
504
736
|
function fromEnv() {
|
|
505
737
|
const cfg = {
|
|
506
|
-
baseURL: process.env.VERDIFAX_BASE_URL
|
|
738
|
+
baseURL: process.env.VERDIFAX_BASE_URL || process.env.VERDIFAX_API_URL || void 0,
|
|
507
739
|
apiKey: process.env.VERDIFAX_API_KEY ?? void 0,
|
|
508
|
-
userAgent: process.env.VERDIFAX_USER_AGENT
|
|
740
|
+
userAgent: process.env.VERDIFAX_USER_AGENT || void 0
|
|
509
741
|
};
|
|
510
742
|
const timeoutRaw = process.env.VERDIFAX_TIMEOUT_MS;
|
|
511
743
|
if (timeoutRaw !== void 0 && timeoutRaw !== "") {
|
|
@@ -529,13 +761,176 @@ function fromEnv() {
|
|
|
529
761
|
}
|
|
530
762
|
return new VerdifaxClient(cfg);
|
|
531
763
|
}
|
|
764
|
+
|
|
765
|
+
// src/environment.ts
|
|
766
|
+
import { execFileSync } from "child_process";
|
|
767
|
+
import { readdirSync, readFileSync } from "fs";
|
|
768
|
+
import { join } from "path";
|
|
769
|
+
var HASH_64 = /^[0-9a-f]{64}$/;
|
|
770
|
+
var GIT_SHA = /^([0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
771
|
+
var PLATFORM_NAMES = { win32: "windows" };
|
|
772
|
+
var ARCH_NAMES = { x64: "amd64", ia32: "386" };
|
|
773
|
+
function packageVersion(dir) {
|
|
774
|
+
try {
|
|
775
|
+
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
776
|
+
return pkg.name && pkg.version ? `${pkg.name}==${pkg.version}` : void 0;
|
|
777
|
+
} catch {
|
|
778
|
+
return void 0;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
function packageDirs(root) {
|
|
782
|
+
const dirs = [];
|
|
783
|
+
for (const entry of readdirSync(root)) {
|
|
784
|
+
if (entry.startsWith(".")) continue;
|
|
785
|
+
if (!entry.startsWith("@")) {
|
|
786
|
+
dirs.push(join(root, entry));
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
for (const scoped of readdirSync(join(root, entry))) {
|
|
790
|
+
dirs.push(join(root, entry, scoped));
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return dirs;
|
|
794
|
+
}
|
|
795
|
+
function detectDependencies() {
|
|
796
|
+
try {
|
|
797
|
+
const pins = packageDirs(join(process.cwd(), "node_modules")).map(packageVersion).filter((pin) => pin !== void 0);
|
|
798
|
+
pins.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
799
|
+
return pins.length > 0 ? pins : void 0;
|
|
800
|
+
} catch {
|
|
801
|
+
return void 0;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
function detectGitSHA() {
|
|
805
|
+
try {
|
|
806
|
+
const sha = execFileSync("git", ["rev-parse", "HEAD"], {
|
|
807
|
+
encoding: "utf8",
|
|
808
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
809
|
+
timeout: 2e3
|
|
810
|
+
}).trim().toLowerCase();
|
|
811
|
+
return GIT_SHA.test(sha) ? sha : void 0;
|
|
812
|
+
} catch {
|
|
813
|
+
return void 0;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
function detectContainerHash() {
|
|
817
|
+
try {
|
|
818
|
+
const text = readFileSync("/proc/self/cgroup", "utf8");
|
|
819
|
+
return text.split("\n").flatMap((line) => line.split("/")).map((segment) => segment.trim()).find((segment) => HASH_64.test(segment));
|
|
820
|
+
} catch {
|
|
821
|
+
return void 0;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
function captureEnvironment(options = {}) {
|
|
825
|
+
const seeds = Object.entries(options.declaredSeeds ?? {}).map(([name, seed]) => `${name}=${seed}`).sort();
|
|
826
|
+
const pinned = options.includeDependencies === false ? void 0 : detectDependencies();
|
|
827
|
+
const git = options.includeGit === false ? void 0 : detectGitSHA();
|
|
828
|
+
const container = options.includeContainer === false ? void 0 : detectContainerHash();
|
|
829
|
+
const os = PLATFORM_NAMES[process.platform] ?? process.platform;
|
|
830
|
+
const arch = ARCH_NAMES[process.arch] ?? process.arch;
|
|
831
|
+
return {
|
|
832
|
+
declared: true,
|
|
833
|
+
runtime_name: "node",
|
|
834
|
+
runtime_version: process.versions.node,
|
|
835
|
+
platform: `${os}/${arch}`,
|
|
836
|
+
...pinned ? { pinned_dependencies: pinned } : {},
|
|
837
|
+
...git ? { git_commit_sha: git } : {},
|
|
838
|
+
...container ? { container_image_hash: container } : {},
|
|
839
|
+
...seeds.length > 0 ? { random_seeds: seeds } : {}
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// src/types.ts
|
|
844
|
+
var RUN_STATUSES = [
|
|
845
|
+
"ok",
|
|
846
|
+
"probe",
|
|
847
|
+
"pepg_deny",
|
|
848
|
+
"ccv_halt",
|
|
849
|
+
"macc_halt",
|
|
850
|
+
"aivp_t4_halt",
|
|
851
|
+
"attestation_gap",
|
|
852
|
+
"stage_error"
|
|
853
|
+
];
|
|
854
|
+
|
|
855
|
+
// src/section0.ts
|
|
856
|
+
import { createHash as createHash2 } from "crypto";
|
|
857
|
+
var DSE_FORMULA_VERSION = "dse.tcu.dispatch.v1";
|
|
858
|
+
var DCAE_PREIMAGE_VERSION = "verdifax.dcae.v1";
|
|
859
|
+
var CRES_PREIMAGE_VERSION = "verdifax.cres.v1";
|
|
860
|
+
var CRES_ENGINE_VERSION = "verdifax-cres/1.0.0";
|
|
861
|
+
function sha256Hex(preimage) {
|
|
862
|
+
return createHash2("sha256").update(Buffer.from(preimage, "utf8")).digest("hex");
|
|
863
|
+
}
|
|
864
|
+
function requireHex64(value, name) {
|
|
865
|
+
try {
|
|
866
|
+
validateHex64(value, name);
|
|
867
|
+
} catch (err) {
|
|
868
|
+
throw new VerdifaxError(
|
|
869
|
+
`verdifax: section0: ${err instanceof Error ? err.message : String(err)}`
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
function dseDispatchHash(fields) {
|
|
874
|
+
requireHex64(fields.program_id, "program_id");
|
|
875
|
+
requireHex64(fields.registry_record_hash, "registry_record_hash");
|
|
876
|
+
return sha256Hex(
|
|
877
|
+
[
|
|
878
|
+
DSE_FORMULA_VERSION,
|
|
879
|
+
fields.envelope_id,
|
|
880
|
+
fields.sequence_id,
|
|
881
|
+
fields.program_id,
|
|
882
|
+
fields.registry_record_hash
|
|
883
|
+
].join(".")
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
function dcaeClosureHash(fields) {
|
|
887
|
+
requireHex64(fields.manifest_hash, "manifest_hash");
|
|
888
|
+
requireHex64(fields.aer_hash, "aer_hash");
|
|
889
|
+
requireHex64(fields.zksp_binding_hash, "zksp_binding_hash");
|
|
890
|
+
return sha256Hex(
|
|
891
|
+
[
|
|
892
|
+
DCAE_PREIMAGE_VERSION,
|
|
893
|
+
fields.manifest_hash,
|
|
894
|
+
fields.aer_hash,
|
|
895
|
+
fields.envelope_id,
|
|
896
|
+
fields.zksp_binding_hash,
|
|
897
|
+
fields.formal_verifier_status
|
|
898
|
+
].join("|")
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
function cresReceiptHash(fields) {
|
|
902
|
+
requireHex64(fields.ciphertext_hash_at_shred, "ciphertext_hash_at_shred");
|
|
903
|
+
return sha256Hex(
|
|
904
|
+
[
|
|
905
|
+
CRES_PREIMAGE_VERSION,
|
|
906
|
+
fields.envelope_id,
|
|
907
|
+
fields.field_path,
|
|
908
|
+
fields.deletion_clock,
|
|
909
|
+
fields.actor_id,
|
|
910
|
+
fields.ciphertext_hash_at_shred,
|
|
911
|
+
fields.dsar_reference,
|
|
912
|
+
fields.engine_version ?? CRES_ENGINE_VERSION
|
|
913
|
+
].join("|")
|
|
914
|
+
);
|
|
915
|
+
}
|
|
532
916
|
export {
|
|
533
917
|
APIError,
|
|
534
918
|
APIKeyRequiredError,
|
|
535
919
|
AuthError,
|
|
920
|
+
CRES_ENGINE_VERSION,
|
|
921
|
+
CRES_PREIMAGE_VERSION,
|
|
922
|
+
DCAE_PREIMAGE_VERSION,
|
|
923
|
+
DSE_FORMULA_VERSION,
|
|
924
|
+
MAX_BATCH_ROWS,
|
|
536
925
|
NotFoundError,
|
|
926
|
+
RUN_STATUSES,
|
|
537
927
|
VerdifaxClient,
|
|
538
928
|
VerdifaxError,
|
|
929
|
+
captureEnvironment,
|
|
930
|
+
cresReceiptHash,
|
|
931
|
+
dcaeClosureHash,
|
|
932
|
+
dseDispatchHash,
|
|
933
|
+
formatProviderPayload,
|
|
539
934
|
fromEnv,
|
|
540
935
|
generateRequestKeyPair,
|
|
541
936
|
requestKeyRegistrationBody,
|