@colophon-claims/verify 0.1.0 → 0.2.1

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.
Files changed (49) hide show
  1. package/README.md +23 -9
  2. package/dist/admission/contracts.d.ts +483 -0
  3. package/dist/admission/contracts.js +285 -0
  4. package/dist/admission/index.d.ts +2 -0
  5. package/dist/admission/index.js +2 -0
  6. package/dist/admission/prompted-commitment.d.ts +19 -0
  7. package/dist/admission/prompted-commitment.js +52 -0
  8. package/dist/admission/prompted-selection.d.ts +24 -0
  9. package/dist/admission/prompted-selection.js +85 -0
  10. package/dist/admission/verification.d.ts +17 -3
  11. package/dist/admission/verification.js +230 -49
  12. package/dist/assets.d.ts +21 -1
  13. package/dist/assets.js +60 -3
  14. package/dist/binding/beacon-binding.d.ts +230 -0
  15. package/dist/binding/beacon-binding.js +325 -0
  16. package/dist/binding/report-face.d.ts +45 -0
  17. package/dist/binding/report-face.js +153 -0
  18. package/dist/cli.js +74 -12
  19. package/dist/index.d.ts +14 -3
  20. package/dist/index.js +16 -3
  21. package/dist/manifest.d.ts +30 -4
  22. package/dist/manifest.js +30 -0
  23. package/dist/materialize.d.ts +7 -0
  24. package/dist/materialize.js +7 -0
  25. package/dist/outcome.d.ts +31 -0
  26. package/dist/outcome.js +45 -0
  27. package/dist/profile/binary-qualification.js +6 -0
  28. package/dist/profile/claim-consistency.d.ts +8 -1
  29. package/dist/profile/claim-consistency.js +4 -4
  30. package/dist/profile/claim.d.ts +203 -1
  31. package/dist/profile/claim.js +192 -43
  32. package/dist/profile/disclosure.d.ts +273 -0
  33. package/dist/profile/disclosure.js +240 -0
  34. package/dist/profile/pinning-evidence.d.ts +1 -1
  35. package/dist/profile/run-results.d.ts +8 -2
  36. package/dist/profile/run-results.js +9 -3
  37. package/dist/profile/task-selection.d.ts +69 -0
  38. package/dist/profile/task-selection.js +140 -0
  39. package/dist/reader-instructions.d.ts +38 -1
  40. package/dist/reader-instructions.js +42 -2
  41. package/dist/schema.d.ts +13 -2
  42. package/dist/schema.js +39 -8
  43. package/dist/signers.d.ts +48 -0
  44. package/dist/signers.js +77 -0
  45. package/dist/verify.d.ts +20 -6
  46. package/dist/verify.js +156 -31
  47. package/dist/version.d.ts +1 -1
  48. package/dist/version.js +1 -1
  49. package/package.json +14 -14
package/dist/verify.js CHANGED
@@ -2,16 +2,17 @@ import { createHash, createPublicKey, verify as verifySignature } from "node:cry
2
2
  import { verifyReport } from "@jinn-network/benchmarking-aggregate";
3
3
  import { verifyEvidenceNativePortableBundle, } from "@jinn-network/benchmarking-evidence";
4
4
  import { exportStaticBundle } from "@jinn-network/benchmarking-interop";
5
- import { cellIdempotencyKey, expectedCellSet, parseBenchmark, parseMatrix, parseReport, parseRun, readRunAnchorIntentExtension, readRunPublicationExtension, } from "@jinn-network/benchmarking-records";
5
+ import { DISCLOSURE_SPECIFICATION_EXTENSION, cellIdempotencyKey, expectedCellSet, parseBenchmark, parseMatrix, parseReport, parseRun, readReportDisclosureExtension, readRunAnchorIntentExtension, readRunPublicationExtension, } from "@jinn-network/benchmarking-records";
6
6
  import { verifyMatrix } from "@jinn-network/benchmarking-run";
7
7
  import { BINARY_JUDGMENT_INSTRUMENT_REQUIREMENT_KEY, BINARY_JUDGMENT_PROFILE_URI, BinaryJudgmentSnapshotProbeSchema, deriveEvaluationTask, parseBinaryJudgmentAnalysisContext, parseBinaryJudgmentInstrument, parseEvaluationSpec, } from "@jinn-network/task-execution-profiles";
8
8
  import { DeliveryRecordSchema, SubmissionRecordSchema, TaskSpecificationSchema } from "@jinn-network/task-execution-protocol";
9
9
  import { canonicalJsonBytes, dssePreAuthEncoding, parseExactDsseEnvelope } from "@jinn-network/trust-core";
10
10
  import { refuse } from "./profile/errors.js";
11
- import { ClaimPackageSchema } from "./profile/claim.js";
11
+ import { BINARY_QUALIFICATION_CLAIM_PACKAGE_SCHEMA_ID, ClaimPackageSchema, DISCLOSED_CLAIM_PACKAGE_SCHEMA_ID, } from "./profile/claim.js";
12
12
  import { buildMethodPortsFromResolver } from "./profile/ports.js";
13
13
  import { didKeyFromEd25519PublicKey } from "./profile/signing.js";
14
14
  import { buildPublicReportTrustDeps } from "./profile/trust.js";
15
+ import { evidenceNativeBundleSigners, legacyBundleSigners } from "./signers.js";
15
16
  import { buildAssemblyPortsFromFacts } from "./profile/assembly-ports.js";
16
17
  import { parseRunPinningEvidenceArtifact, pinningEvidenceFacts, } from "./profile/pinning-evidence.js";
17
18
  import { parsePredictionSnapshotAdmissionReceipt, } from "./profile/admission-receipts.js";
@@ -23,11 +24,13 @@ import { InspectSelectionManifestSchema, isInspectMultiScorerSelection, } from "
23
24
  import { describeInspectRuntimeMethod, } from "./profile/inspect-disclosure.js";
24
25
  import { deriveInspectEvaluationStrategy, INSPECT_SEPARATE_ASSURANCE_LIMITATIONS, inspectLogVerifierMethod, } from "./profile/inspect-assurance.js";
25
26
  import { assertClaimConsistency } from "./profile/claim-consistency.js";
27
+ import { assertTaskSelectionConsistency } from "./profile/task-selection.js";
26
28
  import { buildPublicAssets } from "./assets.js";
27
29
  import { derivePublicComparison } from "./comparison.js";
28
30
  import { verifyBundleSnapshot, } from "./manifest.js";
29
31
  import { PUBLIC_BUNDLE_FILES, PUBLIC_BUNDLE_V4_FILES } from "./materialize.js";
30
- import { BUNDLE_V4_FORMAT, BUNDLE_V5_FORMAT, BUNDLE_V6_FORMAT } from "./manifest.js";
32
+ import { BUNDLE_V4_FORMAT, BUNDLE_V5_FORMAT, BUNDLE_V6_FORMAT, BUNDLE_V7_FORMAT, BUNDLE_V8_FORMAT } from "./manifest.js";
33
+ import { DISCLOSURE_SPECIFICATION_BUNDLE_ROLE, DisclosureProjectionError, assertDisclosureSpecification, deriveDisclosureSpecification, } from "./profile/disclosure.js";
31
34
  import { evaluateIntegrityAnchors, } from "./anchor/check.js";
32
35
  import { ClaimAnchorProjectionError, deriveClaimAnchors } from "./profile/anchor-claims.js";
33
36
  import { verifyBinaryJudgmentAdmissionClosure, } from "./admission/verification.js";
@@ -214,13 +217,54 @@ export function checkItemBankSourceClosure(itemRows, sourceRows) {
214
217
  * public keys. No pathname is reopened after manifest authentication. */
215
218
  export async function verifyPublicBundleSnapshot(bundleDir, deps = {}) {
216
219
  const checked = verifyBundleSnapshot(bundleDir, deps);
220
+ // ── G0, the closure-INDEPENDENT extension guard (disclosure design §7 preamble, issue #2839) ──
221
+ //
222
+ // A Report carrying `DISCLOSURE_SPECIFICATION_EXTENSION` on any format other than `/8` refuses,
223
+ // here, before the format branch — so this holds for `/2`, `/4`, `/5`, `/6`, and `/7` alike, the
224
+ // evidence-native path included.
225
+ //
226
+ // Without it there is a real hole: a `/7` bundle could carry both the extension and the record,
227
+ // have the record's role derived by the extension edge, satisfy the evidence-closure reachability
228
+ // guard, and never be looked at by any check — because the named check runs only on `/8`. G0
229
+ // closes that direction; the evidence closure's own size and per-digest guards close the
230
+ // complementary one (role present, extension absent). The two together are what make the
231
+ // extension and the closure inseparable.
232
+ //
233
+ // Read deliberately at the JSON level rather than through `parseReport`: this must fire on a
234
+ // format whose `report.json` this function never otherwise parses, and it must fire on the
235
+ // extension's PRESENCE, not on its well-formedness.
236
+ if (checked.manifest.format !== BUNDLE_V8_FORMAT) {
237
+ const carriedReport = checked.fileBytes.get("report.json");
238
+ if (carriedReport !== undefined) {
239
+ let parsedReportJson;
240
+ try {
241
+ parsedReportJson = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(carriedReport));
242
+ }
243
+ catch {
244
+ parsedReportJson = undefined;
245
+ }
246
+ if (typeof parsedReportJson === "object" && parsedReportJson !== null && !Array.isArray(parsedReportJson)
247
+ && parsedReportJson[DISCLOSURE_SPECIFICATION_EXTENSION] !== undefined) {
248
+ refuse("record-integrity", "report.json", `a Report carrying ${DISCLOSURE_SPECIFICATION_EXTENSION} is publishable only on`
249
+ + ` ${BUNDLE_V8_FORMAT}, whose disclosure-specification check reads it; this bundle`
250
+ + ` declares ${checked.manifest.format}`);
251
+ }
252
+ }
253
+ }
217
254
  if (checked.manifest.format === BUNDLE_V5_FORMAT) {
218
255
  try {
256
+ const claimPackageBytes = checked.fileBytes.get("claim-package.json");
257
+ if (claimPackageBytes === undefined)
258
+ throw new TypeError("portable bundle is missing claim-package.json");
259
+ const verification = await verifyEvidenceNativePortableBundle({
260
+ files: checked.fileBytes,
261
+ verifySignature: verifyEvidenceNativeSignature,
262
+ });
219
263
  return {
220
- verification: await verifyEvidenceNativePortableBundle({
221
- files: checked.fileBytes,
222
- verifySignature: verifyEvidenceNativeSignature,
223
- }),
264
+ verification: {
265
+ ...verification,
266
+ signers: evidenceNativeBundleSigners(claimPackageBytes, verification.verifiedSignerKeyIds),
267
+ },
224
268
  snapshot: checked,
225
269
  };
226
270
  }
@@ -236,17 +280,25 @@ export async function verifyPublicBundleSnapshot(bundleDir, deps = {}) {
236
280
  };
237
281
  const checks = ["manifest"];
238
282
  const manifestPaths = new Set(checked.manifest.files.map((file) => file.path));
239
- const isV4 = checked.manifest.format === BUNDLE_V4_FORMAT;
240
- // The anchored closure is v2's graph plus `anchors/`, so it takes v2's mandatory member list; the
241
- // binary qualification projection has its own later anchored allocation and is not this one.
242
- const isV6 = checked.manifest.format === BUNDLE_V6_FORMAT;
243
- const mandatoryFiles = isV4 ? PUBLIC_BUNDLE_V4_FILES : PUBLIC_BUNDLE_FILES;
283
+ // Three independent axes, five formats. The qualification axis decides the mandatory member list,
284
+ // the evidence-catalog grammar, and the trust grammar; the anchor axis decides the `anchors/`
285
+ // allowlist and the `integrity-anchors` check; the disclosure axis decides the Report-extension
286
+ // edge and the `disclosure-specification` check. v6 is v2 plus anchors, v7 is v4 plus anchors
287
+ // (issue #3205), v8 is v7 plus disclosure (issue #2839) — no axis reinterprets another.
288
+ const carriesQualification = checked.manifest.format === BUNDLE_V4_FORMAT
289
+ || checked.manifest.format === BUNDLE_V7_FORMAT
290
+ || checked.manifest.format === BUNDLE_V8_FORMAT;
291
+ const carriesAnchors = checked.manifest.format === BUNDLE_V6_FORMAT
292
+ || checked.manifest.format === BUNDLE_V7_FORMAT
293
+ || checked.manifest.format === BUNDLE_V8_FORMAT;
294
+ const carriesDisclosure = checked.manifest.format === BUNDLE_V8_FORMAT;
295
+ const mandatoryFiles = carriesQualification ? PUBLIC_BUNDLE_V4_FILES : PUBLIC_BUNDLE_FILES;
244
296
  for (const path of mandatoryFiles) {
245
297
  if (!manifestPaths.has(path))
246
298
  refuse("record-integrity", path, `mandatory public bundle file "${path}" is missing`);
247
299
  }
248
300
  const evidenceBytes = read("evidence.json");
249
- const evidence = isV4
301
+ const evidence = carriesQualification
250
302
  ? parseJson(evidenceBytes, BundleV4EvidenceCatalogSchema, "evidence.json")
251
303
  : parseJson(evidenceBytes, BundleEvidenceCatalogSchema, "evidence.json");
252
304
  requireCanonical(evidenceBytes, evidence, "evidence.json");
@@ -263,10 +315,10 @@ export async function verifyPublicBundleSnapshot(bundleDir, deps = {}) {
263
315
  if (/^native\/inspect\/[a-f0-9]{64}\.eval$/u.test(path))
264
316
  expectedPaths.add(path);
265
317
  }
266
- // `anchors/<sha256>.bin` is allowlisted only by the closure version that defines it: an anchor
267
- // member in a v2 or v4 bundle is a non-allowlisted file, exactly as it was before this format.
318
+ // `anchors/<sha256>.bin` is allowlisted only by the closure versions that define it: an anchor
319
+ // member in a v2 or v4 bundle is a non-allowlisted file, exactly as it was before these formats.
268
320
  const anchorPaths = [];
269
- if (isV6) {
321
+ if (carriesAnchors) {
270
322
  for (const path of manifestPaths) {
271
323
  if (/^anchors\/[a-f0-9]{64}\.bin$/u.test(path)) {
272
324
  expectedPaths.add(path);
@@ -290,7 +342,7 @@ export async function verifyPublicBundleSnapshot(bundleDir, deps = {}) {
290
342
  declaredRoles.set(record.sha256, new Set(record.roles));
291
343
  }
292
344
  const trustBytes = read("trust/public-keys.json");
293
- const trust = isV4
345
+ const trust = carriesQualification
294
346
  ? parseJson(trustBytes, BundleV4TrustSchema, "trust/public-keys.json")
295
347
  : parseJson(trustBytes, BundleTrustSchema, "trust/public-keys.json");
296
348
  requireCanonical(trustBytes, trust, "trust/public-keys.json");
@@ -300,7 +352,7 @@ export async function verifyPublicBundleSnapshot(bundleDir, deps = {}) {
300
352
  { keyId: entry.keyId, publicKey: publicKey(entry.spkiDerBase64, `trust.evaluators.${entry.evaluator}`) },
301
353
  ]));
302
354
  let qualification;
303
- if (isV4) {
355
+ if (carriesQualification) {
304
356
  const qualificationBytes = read("qualification.json");
305
357
  qualification = parseJson(qualificationBytes, BundleQualificationSchema, "qualification.json");
306
358
  requireCanonical(qualificationBytes, qualification, "qualification.json");
@@ -348,7 +400,7 @@ export async function verifyPublicBundleSnapshot(bundleDir, deps = {}) {
348
400
  // empty section and an omitted one are different claims, and §7.3's declared-but-absent bundle
349
401
  // carries the first.
350
402
  let claimAnchors;
351
- if (isV6) {
403
+ if (carriesAnchors) {
352
404
  const anchorRecords = anchorPaths
353
405
  .map((path) => {
354
406
  const bytes = read(path);
@@ -384,6 +436,33 @@ export async function verifyPublicBundleSnapshot(bundleDir, deps = {}) {
384
436
  throw cause;
385
437
  }
386
438
  }
439
+ // ── The disclosed closure's Report-extension edge and claim projection (issue #2839) ────────
440
+ //
441
+ // Read here, beside the anchors, and for the same two reasons: the evidence-closure walk below
442
+ // needs the edge to derive the record's role, and `assertClaimConsistency` needs the projection to
443
+ // rebuild the claim from the RECORD'S OWN BYTES rather than from the claim under test.
444
+ let disclosureExtensionDigest;
445
+ let claimDisclosure;
446
+ if (carriesDisclosure) {
447
+ const descriptor = readReportDisclosureExtension(report);
448
+ if (descriptor === undefined) {
449
+ refuse("record-integrity", "disclosure-specification", `a ${BUNDLE_V8_FORMAT} bundle must carry ${DISCLOSURE_SPECIFICATION_EXTENSION} on its Report`);
450
+ }
451
+ disclosureExtensionDigest = descriptor.digest.sha256;
452
+ const disclosureBytes = records.get(disclosureExtensionDigest);
453
+ if (disclosureBytes === undefined) {
454
+ refuse("record-integrity", `records/${disclosureExtensionDigest}.bin`, "the bundle does not carry the disclosure-specification record its Report names");
455
+ }
456
+ try {
457
+ claimDisclosure = deriveDisclosureSpecification(disclosureBytes);
458
+ }
459
+ catch (cause) {
460
+ if (cause instanceof DisclosureProjectionError) {
461
+ refuse("record-integrity", `records/${disclosureExtensionDigest}.bin`, cause.message);
462
+ }
463
+ throw cause;
464
+ }
465
+ }
387
466
  const assembly = parseAssembly(read("verification/assembly.jsonl"));
388
467
  const expectedNativePaths = new Set(assembly.header.graph.solveDeliveries.flatMap((delivery) => delivery.outputs
389
468
  .filter((output) => output.name === "inspect-log")
@@ -415,6 +494,18 @@ export async function verifyPublicBundleSnapshot(bundleDir, deps = {}) {
415
494
  const cellsByKey = new Map(assembly.cells.map((cell) => [cell.cellKey, cell]));
416
495
  const coordinatesByKey = new Map(expectedCoordinates.map((coord) => [coord.cellKey, coord]));
417
496
  const expectedRoles = new Map();
497
+ // ── The disclosed closure's one graph edge (issue #2839, design §6.2 / §10.2) ───────────────
498
+ //
499
+ // The Report extension IS the edge: it is the only thing that derives the
500
+ // `disclosure-specification` role for any record. Adding it here — and only on `/8` — is what
501
+ // makes the two refusals in §6.5.2 fire on every other format without a bespoke guard. A
502
+ // standalone disclosure record on a `/7` bundle makes `declaredRoles` exceed `expectedRoles` and
503
+ // trips the size compare; the role appended to an existing graph record's array leaves the sizes
504
+ // equal and trips the per-digest compare instead. Both refusals already existed and cannot be
505
+ // forgotten.
506
+ if (disclosureExtensionDigest !== undefined) {
507
+ addRole(expectedRoles, disclosureExtensionDigest, DISCLOSURE_SPECIFICATION_BUNDLE_ROLE);
508
+ }
418
509
  const evaluationSpecs = new Map();
419
510
  const taskSpecs = new Map();
420
511
  const inspectSelections = new Map();
@@ -1476,6 +1567,11 @@ export async function verifyPublicBundleSnapshot(bundleDir, deps = {}) {
1476
1567
  if (!equalBytes(canonicalJsonBytes(verifiedReport.record), reportBytes))
1477
1568
  refuse("record-integrity", "report-verification", "verified Report payload does not match report.json");
1478
1569
  checks.push("report-verification");
1570
+ // Task-selection provenance (#2980). Runs under the `claim-consistency` check because that is
1571
+ // exactly the question it answers -- does the claim's declared selection survive contact with
1572
+ // the records? -- and because the claim pins the check list byte-for-byte, so a new named check
1573
+ // would be a bundle-format bump rather than an addition.
1574
+ assertTaskSelectionConsistency({ benchmarkRecord: benchmark, runRecord: run });
1479
1575
  assertClaimConsistency({
1480
1576
  claim,
1481
1577
  identities,
@@ -1490,12 +1586,41 @@ export async function verifyPublicBundleSnapshot(bundleDir, deps = {}) {
1490
1586
  : {}),
1491
1587
  ...(assembly.header.rehearsal === undefined ? {} : { rehearsal: assembly.header.rehearsal }),
1492
1588
  ...(claimAnchors === undefined ? {} : { anchors: claimAnchors }),
1589
+ ...(claimDisclosure === undefined ? {} : { disclosure: claimDisclosure }),
1493
1590
  });
1494
1591
  checks.push("claim-consistency");
1495
- // Always present for this closure version, and never for any earlier one: an anchored bundle
1496
- // whose anchors were stripped is a closure failure above, not a shorter check list here.
1497
- if (isV6)
1592
+ // Always present for the anchored closure versions, and never for any earlier one: an anchored
1593
+ // bundle whose anchors were stripped is a closure failure above, not a shorter check list here.
1594
+ if (carriesAnchors)
1498
1595
  checks.push("integrity-anchors");
1596
+ // ── disclosure-specification (issue #2839, design §7 steps 1-9) ────────────────────────────
1597
+ //
1598
+ // Runs after `claim-consistency` because step 10 — projection equality — is delivered BY that
1599
+ // check's existing whole-claim byte-compare rather than by a second comparison here. What is left
1600
+ // for this block is authenticating the record itself and pairing the two claim identifiers.
1601
+ let disclosureReport;
1602
+ if (carriesDisclosure) {
1603
+ // Step 9: claim-id pairing. `qualification.json`'s `claimSchema` names WHICH PROJECTION SHAPE
1604
+ // the qualification graph was built for, and that shape is claim-package/2's on every binary
1605
+ // allocation — so the frozen literal does not move, while `claim-package.json` declares the
1606
+ // closure. Either field carrying the other's value refuses, so the two cannot drift together.
1607
+ if (claim.claimSchema !== DISCLOSED_CLAIM_PACKAGE_SCHEMA_ID) {
1608
+ refuse("record-integrity", "claim-package.json", `a ${BUNDLE_V8_FORMAT} bundle's claim must declare ${DISCLOSED_CLAIM_PACKAGE_SCHEMA_ID}`);
1609
+ }
1610
+ if (qualification?.claimSchema !== BINARY_QUALIFICATION_CLAIM_PACKAGE_SCHEMA_ID) {
1611
+ refuse("record-integrity", "qualification.json", `a ${BUNDLE_V8_FORMAT} bundle's qualification document must keep its frozen`
1612
+ + ` ${BINARY_QUALIFICATION_CLAIM_PACKAGE_SCHEMA_ID} projection literal`);
1613
+ }
1614
+ disclosureReport = assertDisclosureSpecification({
1615
+ extensionDigestSha256: disclosureExtensionDigest,
1616
+ catalogRoles: declaredRoles,
1617
+ recordBytes: records,
1618
+ matrixSha256: identities.matrixSha256,
1619
+ reportAuthor: verifiedReport.record.author,
1620
+ refuse: (path, message) => refuse("record-integrity", path, message),
1621
+ });
1622
+ checks.push("disclosure-specification");
1623
+ }
1499
1624
  const dissentCellKeys = assembly.cells
1500
1625
  .filter((cell) => new Set(cell.verdicts.map((verdict) => verdict.verdict)).size > 1)
1501
1626
  .map((cell) => cell.cellKey)
@@ -1520,17 +1645,15 @@ export async function verifyPublicBundleSnapshot(bundleDir, deps = {}) {
1520
1645
  }),
1521
1646
  dissentCellKeys,
1522
1647
  };
1523
- // Reader v1 remains able to authenticate bundles published before the
1524
- // human comparison projection existed. A bundle must match one complete,
1525
- // deterministic presentation profile; individual assets cannot be mixed.
1526
- const legacyAssets = qualification === undefined ? buildPublicAssets(assetFacts) : undefined;
1527
- const isLegacyPresentation = legacyAssets !== undefined
1528
- && Object.entries(legacyAssets).every(([path, bytes]) => equalBytes(read(path), bytes));
1648
+ // The closure selects the presentation profile and the bundle must byte-match that one
1649
+ // profile completely assets cannot be mixed, and there is no second profile to fall back
1650
+ // to (issue #2984). A qualification-projecting bundle (`/4`, `/7`) renders the
1651
+ // instrument-qualification graph; every other one renders the human comparison, which
1652
+ // `derivePublicComparison` above always produces. The comparison-absent rendering a
1653
+ // pre-comparison producer would have written is therefore no bundle's profile.
1529
1654
  const expectedAssets = qualification !== undefined
1530
1655
  ? buildPublicAssets({ ...assetFacts, binaryQualification: binaryAssetQualification })
1531
- : isLegacyPresentation
1532
- ? legacyAssets
1533
- : buildPublicAssets({ ...assetFacts, comparison });
1656
+ : buildPublicAssets({ ...assetFacts, comparison });
1534
1657
  for (const [path, bytes] of Object.entries(expectedAssets)) {
1535
1658
  if (!equalBytes(read(path), bytes))
1536
1659
  refuse("record-integrity", path, `${path} is not the exact projection of verified public facts`);
@@ -1547,9 +1670,11 @@ export async function verifyPublicBundleSnapshot(bundleDir, deps = {}) {
1547
1670
  format: checked.manifest.format,
1548
1671
  identity: checked.identity,
1549
1672
  checks,
1673
+ signers: legacyBundleSigners(trust, new Set(verdictCatalog.verdicts.map((verdict) => verdict.evaluator))),
1550
1674
  ...identities,
1551
1675
  ...(runtimeMethod === undefined ? {} : { runtimeMethod }),
1552
1676
  ...(anchorReport === undefined ? {} : { anchors: anchorReport }),
1677
+ ...(disclosureReport === undefined ? {} : { disclosure: disclosureReport }),
1553
1678
  ...(qualification === undefined ? {} : {
1554
1679
  qualification: {
1555
1680
  publicationGrade: qualification.publicationGrade,
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Public verifier release line. Kept in one module for output and bundle instructions. */
2
- export declare const VERIFIER_VERSION: "0.1.0";
2
+ export declare const VERIFIER_VERSION: "0.2.1";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Public verifier release line. Kept in one module for output and bundle instructions. */
2
- export const VERIFIER_VERSION = "0.1.0";
2
+ export const VERIFIER_VERSION = "0.2.1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colophon-claims/verify",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Standalone public-bundle verifier for Colophon claims.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -47,28 +47,28 @@
47
47
  "@fontsource-variable/newsreader": "5.3.0",
48
48
  "@fontsource-variable/public-sans": "5.3.0",
49
49
  "@fontsource/ibm-plex-mono": "5.3.0",
50
- "@jinn-network/benchmarking-aggregate": "0.1.0-canary.sha.1ed36166faf16ea4b96b021ceff0397f83a0a80c",
51
- "@jinn-network/benchmarking-evidence": "0.1.0-canary.sha.1ed36166faf16ea4b96b021ceff0397f83a0a80c",
52
- "@jinn-network/benchmarking-interop": "0.1.0-canary.sha.1ed36166faf16ea4b96b021ceff0397f83a0a80c",
53
- "@jinn-network/benchmarking-local": "0.1.0-canary.sha.1ed36166faf16ea4b96b021ceff0397f83a0a80c",
54
- "@jinn-network/benchmarking-protocol": "0.1.0-canary.sha.1ed36166faf16ea4b96b021ceff0397f83a0a80c",
55
- "@jinn-network/benchmarking-records": "0.1.0-canary.sha.1ed36166faf16ea4b96b021ceff0397f83a0a80c",
56
- "@jinn-network/benchmarking-run": "0.1.0-canary.sha.1ed36166faf16ea4b96b021ceff0397f83a0a80c",
57
- "@jinn-network/task-admission": "0.1.0-canary.sha.1ed36166faf16ea4b96b021ceff0397f83a0a80c",
58
- "@jinn-network/task-execution-profiles": "0.1.0-canary.sha.1ed36166faf16ea4b96b021ceff0397f83a0a80c",
59
- "@jinn-network/task-execution-protocol": "0.1.0-canary.sha.1ed36166faf16ea4b96b021ceff0397f83a0a80c",
60
- "@jinn-network/trust-core": "0.1.0-canary.sha.1ed36166faf16ea4b96b021ceff0397f83a0a80c",
50
+ "@jinn-network/benchmarking-aggregate": "0.1.0-canary.sha.0533a224cf99f06d7facf0c23455f2781a5b9e62",
51
+ "@jinn-network/benchmarking-evidence": "0.1.0-canary.sha.0533a224cf99f06d7facf0c23455f2781a5b9e62",
52
+ "@jinn-network/benchmarking-interop": "0.1.0-canary.sha.0533a224cf99f06d7facf0c23455f2781a5b9e62",
53
+ "@jinn-network/benchmarking-local": "0.1.0-canary.sha.0533a224cf99f06d7facf0c23455f2781a5b9e62",
54
+ "@jinn-network/benchmarking-protocol": "0.1.0-canary.sha.0533a224cf99f06d7facf0c23455f2781a5b9e62",
55
+ "@jinn-network/benchmarking-records": "0.1.0-canary.sha.0533a224cf99f06d7facf0c23455f2781a5b9e62",
56
+ "@jinn-network/benchmarking-run": "0.1.0-canary.sha.0533a224cf99f06d7facf0c23455f2781a5b9e62",
57
+ "@jinn-network/task-admission": "0.1.0-canary.sha.0533a224cf99f06d7facf0c23455f2781a5b9e62",
58
+ "@jinn-network/task-execution-profiles": "0.1.0-canary.sha.0533a224cf99f06d7facf0c23455f2781a5b9e62",
59
+ "@jinn-network/task-execution-protocol": "0.1.0-canary.sha.0533a224cf99f06d7facf0c23455f2781a5b9e62",
60
+ "@jinn-network/trust-core": "0.1.0-canary.sha.0533a224cf99f06d7facf0c23455f2781a5b9e62",
61
61
  "zod": "4.4.3"
62
62
  },
63
63
  "resolutions": {
64
64
  "@noble/hashes": "2.2.0"
65
65
  },
66
66
  "devDependencies": {
67
- "@jinn-network/trust-testing": "0.1.0-canary.sha.1ed36166faf16ea4b96b021ceff0397f83a0a80c",
67
+ "@jinn-network/trust-testing": "0.1.0-canary.sha.0533a224cf99f06d7facf0c23455f2781a5b9e62",
68
68
  "@types/node": "^22.0.0",
69
69
  "ajv": "8.17.1",
70
70
  "typescript": "^5.9.3",
71
71
  "vitest": "^4.1.8"
72
72
  },
73
- "gitHead": "2f249073718111afd810127ff7bbbc19b206dc93"
73
+ "gitHead": "56d36e4167d395ee496b5b4baa72d3527970060f"
74
74
  }