@fluxpointstudios/orynq-sdk-process-trace 0.1.0 → 0.3.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.cjs CHANGED
@@ -9,7 +9,11 @@ var DEFAULT_EVENT_VISIBILITY = {
9
9
  decision: "private",
10
10
  observation: "public",
11
11
  error: "private",
12
- custom: "private"
12
+ custom: "private",
13
+ // Governance provenance is meant to be auditable by third parties.
14
+ "governance-attestation": "public",
15
+ // Tool responses may carry PII/secrets; only hashes + signature are required.
16
+ "tool-receipt": "private"
13
17
  };
14
18
  var HASH_DOMAIN_PREFIXES = {
15
19
  event: "poi-trace:event:v1|",
@@ -18,7 +22,11 @@ var HASH_DOMAIN_PREFIXES = {
18
22
  leaf: "poi-trace:leaf:v1|",
19
23
  node: "poi-trace:node:v1|",
20
24
  manifest: "poi-trace:manifest:v1|",
21
- root: "poi-trace:root:v1|"
25
+ root: "poi-trace:root:v1|",
26
+ /** Model/data manifest commitment (pre-execution pinning). */
27
+ modelManifest: "poi-trace:model-manifest:v1|",
28
+ /** Governance-attestation signing preimage. */
29
+ governance: "poi-trace:governance:v1|"
22
30
  };
23
31
  var GENESIS_SEED = "genesis";
24
32
  async function computeEventHash(event) {
@@ -70,7 +78,7 @@ function constantTimeCompare(a, b) {
70
78
  }
71
79
  return result === 0;
72
80
  }
73
- async function computeRootHash(rollingHash, spans) {
81
+ async function computeRootHash(rollingHash, spans, modelManifestHash) {
74
82
  const sortedSpans = [...spans].sort((a, b) => a.spanSeq - b.spanSeq);
75
83
  const spanHashes = sortedSpans.map((span) => {
76
84
  if (!span.hash) {
@@ -82,6 +90,9 @@ async function computeRootHash(rollingHash, spans) {
82
90
  if (spanHashes.length > 0) {
83
91
  input += "|" + spanHashes.join("|");
84
92
  }
93
+ if (modelManifestHash !== void 0 && modelManifestHash.length > 0) {
94
+ input += "|manifest:" + modelManifestHash;
95
+ }
85
96
  return utils.sha256StringHex(input);
86
97
  }
87
98
  async function computeEventHashes(events) {
@@ -283,6 +294,108 @@ async function verifySpanInclusion(proof, span, events) {
283
294
  }
284
295
  return verifyMerkleProof(proof);
285
296
  }
297
+ async function computeModelManifestHash(manifest) {
298
+ const canonical = utils.canonicalize(manifest);
299
+ return utils.sha256StringHex(HASH_DOMAIN_PREFIXES.modelManifest + canonical);
300
+ }
301
+ function validateModelManifest(manifest) {
302
+ if (typeof manifest !== "object" || manifest === null) {
303
+ throw new Error("ModelManifest must be an object");
304
+ }
305
+ if (typeof manifest.modelHash !== "string" || manifest.modelHash.length === 0) {
306
+ throw new Error("ModelManifest.modelHash is required and must be a non-empty string");
307
+ }
308
+ return manifest;
309
+ }
310
+ function freezeModelManifest(manifest) {
311
+ if (manifest.metadata) {
312
+ Object.freeze(manifest.metadata);
313
+ }
314
+ return Object.freeze(manifest);
315
+ }
316
+ async function fingerprint(parts) {
317
+ const joined = parts.filter((p) => typeof p === "string" && p.length > 0).join("\0");
318
+ return "sha256:" + await utils.sha256StringHex("orynq:model-fingerprint:v1|" + joined);
319
+ }
320
+ async function resolveSystemPromptHash(systemPrompt, systemPromptHash) {
321
+ if (systemPromptHash !== void 0) return systemPromptHash;
322
+ if (systemPrompt !== void 0) {
323
+ return "sha256:" + await utils.sha256StringHex("orynq:system-prompt:v1|" + systemPrompt);
324
+ }
325
+ return void 0;
326
+ }
327
+ function assemble(base) {
328
+ const m = { modelHash: base.modelHash, framework: base.framework };
329
+ if (base.modelId !== void 0) m.modelId = base.modelId;
330
+ if (base.revision !== void 0) m.revision = base.revision;
331
+ if (base.tokenizerHash !== void 0) m.tokenizerHash = base.tokenizerHash;
332
+ if (base.systemPromptHash !== void 0) m.systemPromptHash = base.systemPromptHash;
333
+ if (base.trainingDataManifest !== void 0) m.trainingDataManifest = base.trainingDataManifest;
334
+ if (base.metadata !== void 0) m.metadata = base.metadata;
335
+ return m;
336
+ }
337
+ async function manifestFromHuggingFace(opts) {
338
+ if (!opts.modelId) throw new Error("manifestFromHuggingFace: modelId is required");
339
+ const modelHash = await fingerprint(["huggingface", opts.modelId, opts.revision]);
340
+ const systemPromptHash = await resolveSystemPromptHash(opts.systemPrompt, opts.systemPromptHash);
341
+ return assemble({
342
+ modelHash,
343
+ framework: "huggingface",
344
+ modelId: opts.modelId,
345
+ revision: opts.revision,
346
+ tokenizerHash: opts.tokenizerHash,
347
+ systemPromptHash,
348
+ trainingDataManifest: opts.trainingDataManifest,
349
+ metadata: opts.metadata
350
+ });
351
+ }
352
+ async function manifestFromOpenAI(opts) {
353
+ if (!opts.model) throw new Error("manifestFromOpenAI: model is required");
354
+ const modelHash = await fingerprint(["openai", opts.model, opts.snapshotId]);
355
+ const systemPromptHash = await resolveSystemPromptHash(opts.systemPrompt, opts.systemPromptHash);
356
+ return assemble({
357
+ modelHash,
358
+ framework: "openai",
359
+ modelId: opts.model,
360
+ revision: opts.snapshotId,
361
+ tokenizerHash: opts.tokenizerHash,
362
+ systemPromptHash,
363
+ trainingDataManifest: opts.trainingDataManifest,
364
+ metadata: opts.metadata
365
+ });
366
+ }
367
+ async function manifestFromAnthropic(opts) {
368
+ if (!opts.model) throw new Error("manifestFromAnthropic: model is required");
369
+ const modelHash = await fingerprint(["anthropic", opts.model, opts.snapshotId]);
370
+ const systemPromptHash = await resolveSystemPromptHash(opts.systemPrompt, opts.systemPromptHash);
371
+ return assemble({
372
+ modelHash,
373
+ framework: "anthropic",
374
+ modelId: opts.model,
375
+ revision: opts.snapshotId,
376
+ tokenizerHash: opts.tokenizerHash,
377
+ systemPromptHash,
378
+ trainingDataManifest: opts.trainingDataManifest,
379
+ metadata: opts.metadata
380
+ });
381
+ }
382
+ async function manifestFromCheckpoint(filepath, opts = {}) {
383
+ if (!filepath) throw new Error("manifestFromCheckpoint: filepath is required");
384
+ const { readFile } = await import('fs/promises');
385
+ const bytes = await readFile(filepath);
386
+ const modelHash = "sha256:" + await utils.sha256Hex(new Uint8Array(bytes));
387
+ const systemPromptHash = await resolveSystemPromptHash(opts.systemPrompt, opts.systemPromptHash);
388
+ return assemble({
389
+ modelHash,
390
+ framework: "checkpoint",
391
+ modelId: opts.modelId ?? filepath,
392
+ revision: opts.revision,
393
+ tokenizerHash: opts.tokenizerHash,
394
+ systemPromptHash,
395
+ trainingDataManifest: opts.trainingDataManifest,
396
+ metadata: opts.metadata
397
+ });
398
+ }
286
399
 
287
400
  // src/trace-builder.ts
288
401
  async function createTrace(opts) {
@@ -312,6 +425,19 @@ async function createTrace(opts) {
312
425
  description: opts.description
313
426
  };
314
427
  }
428
+ const strict = opts.strict ?? false;
429
+ if (strict) {
430
+ run.strict = true;
431
+ }
432
+ if (opts.manifest !== void 0) {
433
+ const manifest = validateModelManifest(opts.manifest);
434
+ run.modelManifestHash = await computeModelManifestHash(manifest);
435
+ run.modelManifest = freezeModelManifest(manifest);
436
+ } else if (strict) {
437
+ throw new Error(
438
+ "createTrace: strict mode requires a `manifest` to be pinned before execution"
439
+ );
440
+ }
315
441
  return run;
316
442
  }
317
443
  function addSpan(run, opts) {
@@ -432,6 +558,16 @@ async function finalizeTrace(run) {
432
558
  if (isFinalized(run)) {
433
559
  throw new Error("Trace run is already finalized");
434
560
  }
561
+ if (run.modelManifest === void 0) {
562
+ if (run.strict) {
563
+ throw new Error(
564
+ "finalizeTrace: strict mode requires a model manifest pinned at createTrace() time"
565
+ );
566
+ }
567
+ console.warn(
568
+ "[orynq] finalizeTrace: no model manifest was pinned \u2014 model/data immutability is NOT proven for this trace. Pass `manifest` to createTrace() (and `strict: true` to enforce). This will become an error in v1.0."
569
+ );
570
+ }
435
571
  for (const span of run.spans) {
436
572
  if (span.status === "running") {
437
573
  await closeSpan(run, span.id, "completed");
@@ -444,7 +580,11 @@ async function finalizeTrace(run) {
444
580
  const endTime = new Date(endedAt).getTime();
445
581
  run.durationMs = endTime - startTime;
446
582
  const merkleTree = await buildSpanMerkleTree(run.spans, run.events);
447
- const rootHash = await computeRootHash(run.rollingHash, run.spans);
583
+ const rootHash = await computeRootHash(
584
+ run.rollingHash,
585
+ run.spans,
586
+ run.modelManifestHash
587
+ );
448
588
  run.rootHash = rootHash;
449
589
  const publicView = createPublicView(run, merkleTree.rootHash);
450
590
  const bundle = {
@@ -454,6 +594,12 @@ async function finalizeTrace(run) {
454
594
  merkleRoot: merkleTree.rootHash,
455
595
  rootHash
456
596
  };
597
+ if (run.modelManifestHash !== void 0) {
598
+ bundle.modelManifestHash = run.modelManifestHash;
599
+ }
600
+ if (run.modelManifest !== void 0) {
601
+ bundle.modelManifest = run.modelManifest;
602
+ }
457
603
  return bundle;
458
604
  }
459
605
  function createPublicView(run, merkleRoot) {
@@ -495,6 +641,12 @@ function createPublicView(run, merkleRoot) {
495
641
  publicSpans,
496
642
  redactedSpanHashes
497
643
  };
644
+ if (run.modelManifestHash !== void 0) {
645
+ publicView.modelManifestHash = run.modelManifestHash;
646
+ }
647
+ if (run.modelManifest !== void 0) {
648
+ publicView.modelManifest = run.modelManifest;
649
+ }
498
650
  return publicView;
499
651
  }
500
652
  function getEventCount(run) {
@@ -517,6 +669,302 @@ function getEventsByKind(run, kind) {
517
669
  (e) => e.kind === kind
518
670
  );
519
671
  }
672
+
673
+ // src/governance.ts
674
+ function governanceAttestationPreimage(fields) {
675
+ const enc = new TextEncoder();
676
+ const domain = enc.encode(HASH_DOMAIN_PREFIXES.governance);
677
+ const parts = [
678
+ fields.runId,
679
+ fields.role,
680
+ fields.policyRef,
681
+ fields.decisionRef,
682
+ fields.signedAt
683
+ ].map((v) => enc.encode(v));
684
+ const total = domain.length + parts.reduce((n, p) => n + 4 + p.length, 0);
685
+ const out = new Uint8Array(total);
686
+ let off = 0;
687
+ out.set(domain, off);
688
+ off += domain.length;
689
+ for (const p of parts) {
690
+ out[off] = p.length >>> 24 & 255;
691
+ out[off + 1] = p.length >>> 16 & 255;
692
+ out[off + 2] = p.length >>> 8 & 255;
693
+ out[off + 3] = p.length & 255;
694
+ off += 4;
695
+ out.set(p, off);
696
+ off += p.length;
697
+ }
698
+ return out;
699
+ }
700
+ async function addGovernanceAttestation(run, spanId, opts) {
701
+ if (!opts.role) throw new Error("addGovernanceAttestation: role is required");
702
+ if (!opts.policyRef) throw new Error("addGovernanceAttestation: policyRef is required");
703
+ if (!opts.decisionRef) throw new Error("addGovernanceAttestation: decisionRef is required");
704
+ if (!opts.signer) throw new Error("addGovernanceAttestation: signer is required");
705
+ if (opts.signer.signatureScheme === "eip712" && opts.eip712 === void 0) {
706
+ throw new Error(
707
+ "addGovernanceAttestation: an `eip712` binding is required for eip712 signers"
708
+ );
709
+ }
710
+ const signedAt = opts.signedAt ?? (/* @__PURE__ */ new Date()).toISOString();
711
+ const fields = {
712
+ role: opts.role,
713
+ policyRef: opts.policyRef,
714
+ decisionRef: opts.decisionRef,
715
+ signedAt,
716
+ runId: run.id
717
+ };
718
+ const preimage = governanceAttestationPreimage(fields);
719
+ const ctx = opts.eip712 ? { preimage, fields, eip712: opts.eip712 } : { preimage, fields };
720
+ const signature = await opts.signer.sign(ctx);
721
+ const event = {
722
+ kind: "governance-attestation",
723
+ visibility: opts.visibility ?? "public",
724
+ role: opts.role,
725
+ policyRef: opts.policyRef,
726
+ decisionRef: opts.decisionRef,
727
+ attestor: {
728
+ address: opts.signer.address,
729
+ signatureScheme: opts.signer.signatureScheme
730
+ },
731
+ signature,
732
+ signedAt,
733
+ ...opts.eip712 ? { eip712: opts.eip712 } : {}
734
+ };
735
+ const recorded = await addEvent(run, spanId, event);
736
+ return recorded;
737
+ }
738
+ var polkadotPromise = null;
739
+ async function loadPolkadot() {
740
+ if (!polkadotPromise) {
741
+ polkadotPromise = (async () => {
742
+ let crypto2;
743
+ let util;
744
+ try {
745
+ crypto2 = await import('@polkadot/util-crypto');
746
+ util = await import('@polkadot/util');
747
+ } catch {
748
+ throw new Error(
749
+ "Built-in sr25519/ed25519 governance support requires the optional peer dependencies '@polkadot/util-crypto' and '@polkadot/util'. Install them, or pass a custom GovernanceSigner / verifier."
750
+ );
751
+ }
752
+ await crypto2.cryptoWaitReady();
753
+ return { crypto: crypto2, util };
754
+ })();
755
+ }
756
+ return polkadotPromise;
757
+ }
758
+ var SS58_PREFIX = 42;
759
+ function resolveSeed(seed, util) {
760
+ if (typeof seed === "string") {
761
+ return util.hexToU8a(seed.startsWith("0x") ? seed : "0x" + seed);
762
+ }
763
+ return seed;
764
+ }
765
+ async function createSr25519GovernanceSigner(opts) {
766
+ const { crypto: crypto2, util } = await loadPolkadot();
767
+ let publicKey;
768
+ let secretKey;
769
+ if (opts.secretKey && opts.publicKey) {
770
+ publicKey = opts.publicKey;
771
+ secretKey = opts.secretKey;
772
+ } else if (opts.seed !== void 0) {
773
+ const pair = crypto2.sr25519PairFromSeed(resolveSeed(opts.seed, util));
774
+ publicKey = pair.publicKey;
775
+ secretKey = pair.secretKey;
776
+ } else {
777
+ throw new Error("createSr25519GovernanceSigner: provide `seed` or `secretKey`+`publicKey`");
778
+ }
779
+ const address = opts.address ?? crypto2.encodeAddress(publicKey, opts.ss58Format ?? SS58_PREFIX);
780
+ return {
781
+ address,
782
+ signatureScheme: "sr25519",
783
+ sign(ctx) {
784
+ return util.u8aToHex(crypto2.sr25519Sign(ctx.preimage, { publicKey, secretKey }));
785
+ }
786
+ };
787
+ }
788
+ async function createEd25519GovernanceSigner(opts) {
789
+ const { crypto: crypto2, util } = await loadPolkadot();
790
+ let publicKey;
791
+ let secretKey;
792
+ if (opts.secretKey && opts.publicKey) {
793
+ publicKey = opts.publicKey;
794
+ secretKey = opts.secretKey;
795
+ } else if (opts.seed !== void 0) {
796
+ const pair = crypto2.ed25519PairFromSeed(resolveSeed(opts.seed, util));
797
+ publicKey = pair.publicKey;
798
+ secretKey = pair.secretKey;
799
+ } else {
800
+ throw new Error("createEd25519GovernanceSigner: provide `seed` or `secretKey`+`publicKey`");
801
+ }
802
+ const address = opts.address ?? crypto2.encodeAddress(publicKey, opts.ss58Format ?? SS58_PREFIX);
803
+ return {
804
+ address,
805
+ signatureScheme: "ed25519",
806
+ sign(ctx) {
807
+ return util.u8aToHex(crypto2.ed25519Sign(ctx.preimage, { publicKey, secretKey }));
808
+ }
809
+ };
810
+ }
811
+ async function verifyGovernanceAttestations(bundle, opts = {}) {
812
+ const events = bundle.privateRun.events.filter(
813
+ (e) => e.kind === "governance-attestation"
814
+ );
815
+ const runId = bundle.privateRun.id;
816
+ const summaries = [];
817
+ for (const event of events) {
818
+ const scheme = event.attestor.signatureScheme;
819
+ const preimage = governanceAttestationPreimage({
820
+ role: event.role,
821
+ policyRef: event.policyRef,
822
+ decisionRef: event.decisionRef,
823
+ signedAt: event.signedAt,
824
+ runId
825
+ });
826
+ const base = {
827
+ eventId: event.id,
828
+ role: event.role,
829
+ attestor: event.attestor.address,
830
+ scheme,
831
+ policyRef: event.policyRef,
832
+ decisionRef: event.decisionRef
833
+ };
834
+ const authorized = attestorAuthorized(event.attestor.address, event.role, opts);
835
+ if (!authorized) {
836
+ summaries.push({
837
+ ...base,
838
+ authorized: false,
839
+ verified: false,
840
+ error: opts.authorizedAttestors === void 0 && opts.authorizedAttestorsByRole === void 0 ? "no authorized-attestor allow-list supplied \u2014 governance verification fails closed (pass authorizedAttestors)" : `attestor ${event.attestor.address} is not authorized for role "${event.role}"`
841
+ });
842
+ continue;
843
+ }
844
+ try {
845
+ const override = opts.verifiers?.[scheme];
846
+ let signatureValid;
847
+ if (override) {
848
+ signatureValid = await override(event, { preimage, runId });
849
+ } else if (scheme === "sr25519" || scheme === "ed25519") {
850
+ signatureValid = await verifySubstrateSignature(scheme, event, preimage);
851
+ } else {
852
+ summaries.push({
853
+ ...base,
854
+ authorized: true,
855
+ verified: false,
856
+ error: `no verifier registered for scheme "${scheme}" (pass one via verifiers)`
857
+ });
858
+ continue;
859
+ }
860
+ summaries.push({ ...base, authorized: true, verified: signatureValid });
861
+ } catch (error) {
862
+ summaries.push({
863
+ ...base,
864
+ authorized: true,
865
+ verified: false,
866
+ error: error instanceof Error ? error.message : String(error)
867
+ });
868
+ }
869
+ }
870
+ return summaries;
871
+ }
872
+ function attestorAuthorized(address, role, opts) {
873
+ const norm = (s) => s.toLowerCase();
874
+ const roleList = opts.authorizedAttestorsByRole?.[role];
875
+ if (roleList !== void 0) {
876
+ return roleList.map(norm).includes(norm(address));
877
+ }
878
+ if (opts.authorizedAttestors !== void 0) {
879
+ return opts.authorizedAttestors.map(norm).includes(norm(address));
880
+ }
881
+ return false;
882
+ }
883
+ async function verifySubstrateSignature(scheme, event, preimage) {
884
+ const { crypto: crypto2, util } = await loadPolkadot();
885
+ const publicKey = crypto2.decodeAddress(event.attestor.address);
886
+ const sig = util.hexToU8a(
887
+ event.signature.startsWith("0x") ? event.signature : "0x" + event.signature
888
+ );
889
+ return scheme === "sr25519" ? crypto2.sr25519Verify(preimage, sig, publicKey) : crypto2.ed25519Verify(preimage, sig, publicKey);
890
+ }
891
+ var REQUIRED_EIP712_FIELDS = ["role", "policyRef", "decisionRef", "runId", "signedAt"];
892
+ var DEFAULT_EIP712_FRESHNESS_MS = 24 * 60 * 6e4;
893
+ function createEip712GovernanceVerifier(deps) {
894
+ if (deps.expectedDomain === void 0 || deps.expectedPrimaryType === void 0 || deps.expectedTypes === void 0) {
895
+ throw new Error(
896
+ "createEip712GovernanceVerifier: expectedDomain, expectedPrimaryType, and expectedTypes are required \u2014 an unpinned verifier accepts an empty attacker-signed struct (forgery)"
897
+ );
898
+ }
899
+ const declared = deps.expectedTypes[deps.expectedPrimaryType];
900
+ if (!declared) {
901
+ throw new Error(
902
+ `createEip712GovernanceVerifier: expectedTypes has no entry for primaryType "${deps.expectedPrimaryType}"`
903
+ );
904
+ }
905
+ const declaredNames = new Set(declared.map((f) => f.name));
906
+ const missing = REQUIRED_EIP712_FIELDS.filter((f) => !declaredNames.has(f));
907
+ if (missing.length > 0) {
908
+ throw new Error(
909
+ `createEip712GovernanceVerifier: the pinned "${deps.expectedPrimaryType}" type must include ${missing.join(", ")} so the signature commits to them`
910
+ );
911
+ }
912
+ return async (event, context) => {
913
+ if (!event.eip712) {
914
+ throw new Error("eip712 governance attestation is missing its `eip712` binding");
915
+ }
916
+ if (event.eip712.primaryType !== deps.expectedPrimaryType) {
917
+ return false;
918
+ }
919
+ if (!domainMatches(deps.expectedDomain, event.eip712.domain)) {
920
+ return false;
921
+ }
922
+ if (!typesMatch(deps.expectedTypes, event.eip712.types)) {
923
+ return false;
924
+ }
925
+ const signature = event.signature.startsWith("0x") ? event.signature : "0x" + event.signature;
926
+ const message = event.eip712.message ?? {};
927
+ const sigValid = await deps.verifyTypedData({
928
+ address: event.attestor.address,
929
+ domain: event.eip712.domain,
930
+ types: event.eip712.types,
931
+ primaryType: event.eip712.primaryType,
932
+ message,
933
+ signature
934
+ });
935
+ if (!sigValid) return false;
936
+ const claimBound = message.role === event.role && message.policyRef === event.policyRef && message.decisionRef === event.decisionRef && message.runId === context.runId && message.signedAt === event.signedAt;
937
+ if (!claimBound) return false;
938
+ const toleranceMs = deps.freshnessToleranceMs ?? DEFAULT_EIP712_FRESHNESS_MS;
939
+ const nowMs = deps.nowMs ?? Date.now();
940
+ const signedAtMs = Date.parse(String(message.signedAt));
941
+ if (!Number.isFinite(signedAtMs)) return false;
942
+ if (Math.abs(nowMs - signedAtMs) > toleranceMs) return false;
943
+ return true;
944
+ };
945
+ }
946
+ function domainMatches(expected, actual) {
947
+ const expectedKeys = Object.keys(expected);
948
+ const actualKeys = Object.keys(actual);
949
+ if (actualKeys.length !== expectedKeys.length) return false;
950
+ for (const key of expectedKeys) {
951
+ if (actual[key] !== expected[key]) return false;
952
+ }
953
+ return true;
954
+ }
955
+ function typesMatch(expected, actual) {
956
+ const norm = (t) => JSON.stringify(
957
+ Object.fromEntries(
958
+ Object.keys(t).sort().map((k) => [
959
+ k,
960
+ [...t[k]].sort((a, b) => a.name.localeCompare(b.name)).map((f) => `${f.name}:${f.type}`)
961
+ ])
962
+ )
963
+ );
964
+ return norm(expected) === norm(actual);
965
+ }
966
+
967
+ // src/bundle.ts
520
968
  function isPublicSpan(span) {
521
969
  return span.visibility === "public";
522
970
  }
@@ -605,7 +1053,7 @@ function createPublicView2(run, merkleRoot) {
605
1053
  function extractPublicView(bundle) {
606
1054
  return bundle.publicView;
607
1055
  }
608
- async function verifyBundle(bundle) {
1056
+ async function verifyBundle(bundle, options = {}) {
609
1057
  const errors = [];
610
1058
  const warnings = [];
611
1059
  const checks = {
@@ -650,7 +1098,11 @@ async function verifyBundle(bundle) {
650
1098
  );
651
1099
  }
652
1100
  try {
653
- const computedRootHash = await computeRootHash(run.rollingHash, run.spans);
1101
+ const computedRootHash = await computeRootHash(
1102
+ run.rollingHash,
1103
+ run.spans,
1104
+ run.modelManifestHash
1105
+ );
654
1106
  if (computedRootHash === bundle.rootHash) {
655
1107
  checks.rootHashValid = true;
656
1108
  } else {
@@ -677,6 +1129,75 @@ async function verifyBundle(bundle) {
677
1129
  `Failed to compute Merkle root: ${error instanceof Error ? error.message : String(error)}`
678
1130
  );
679
1131
  }
1132
+ const hasManifest = run.modelManifest !== void 0;
1133
+ const hasManifestHash = run.modelManifestHash !== void 0 && run.modelManifestHash.length > 0;
1134
+ if (!hasManifest && !hasManifestHash) {
1135
+ checks.modelManifestValid = true;
1136
+ } else {
1137
+ let manifestBindingValid = true;
1138
+ if (hasManifest && !hasManifestHash) {
1139
+ manifestBindingValid = false;
1140
+ errors.push(
1141
+ "Model manifest present but modelManifestHash (its commitment) is missing"
1142
+ );
1143
+ } else if (!hasManifest && hasManifestHash) {
1144
+ manifestBindingValid = false;
1145
+ errors.push(
1146
+ "modelManifestHash present but the model manifest itself is missing"
1147
+ );
1148
+ } else if (run.modelManifest !== void 0) {
1149
+ try {
1150
+ const recomputed = await computeModelManifestHash(run.modelManifest);
1151
+ if (recomputed !== run.modelManifestHash) {
1152
+ manifestBindingValid = false;
1153
+ errors.push(
1154
+ `Model manifest hash mismatch: recorded ${run.modelManifestHash}, computed ${recomputed}`
1155
+ );
1156
+ }
1157
+ } catch (error) {
1158
+ manifestBindingValid = false;
1159
+ errors.push(
1160
+ `Failed to recompute model manifest hash: ${error instanceof Error ? error.message : String(error)}`
1161
+ );
1162
+ }
1163
+ }
1164
+ if (manifestBindingValid && !checks.rootHashValid) {
1165
+ manifestBindingValid = false;
1166
+ errors.push(
1167
+ "Model manifest is not bound into the committed root hash"
1168
+ );
1169
+ }
1170
+ checks.modelManifestValid = manifestBindingValid;
1171
+ }
1172
+ const pvManifest = bundle.publicView.modelManifest;
1173
+ const pvManifestHash = bundle.publicView.modelManifestHash;
1174
+ if (pvManifestHash !== void 0 && pvManifestHash !== run.modelManifestHash) {
1175
+ checks.modelManifestValid = false;
1176
+ errors.push(
1177
+ `PublicView modelManifestHash (${pvManifestHash}) does not match the bound commitment (${run.modelManifestHash})`
1178
+ );
1179
+ }
1180
+ if (pvManifest !== void 0 && run.modelManifest === void 0) {
1181
+ checks.modelManifestValid = false;
1182
+ errors.push("PublicView carries a model manifest but the bound run has none");
1183
+ }
1184
+ if (pvManifest !== void 0) {
1185
+ try {
1186
+ const recomputed = await computeModelManifestHash(pvManifest);
1187
+ const expected = pvManifestHash ?? run.modelManifestHash;
1188
+ if (expected !== void 0 && recomputed !== expected) {
1189
+ checks.modelManifestValid = false;
1190
+ errors.push(
1191
+ `PublicView model manifest hash mismatch: recorded ${expected}, computed ${recomputed}`
1192
+ );
1193
+ }
1194
+ } catch (error) {
1195
+ checks.modelManifestValid = false;
1196
+ errors.push(
1197
+ `Failed to recompute publicView model manifest hash: ${error instanceof Error ? error.message : String(error)}`
1198
+ );
1199
+ }
1200
+ }
680
1201
  if (bundle.publicView.publicSpans.length === 0 && run.spans.length > 0) {
681
1202
  warnings.push(
682
1203
  "No public spans in bundle. The public view will be empty. Consider marking some spans as public for transparency."
@@ -687,7 +1208,39 @@ async function verifyBundle(bundle) {
687
1208
  `Status mismatch between publicView (${bundle.publicView.status}) and privateRun (${run.status})`
688
1209
  );
689
1210
  }
690
- const valid = checks.rollingHashValid && checks.rootHashValid && checks.merkleRootValid && checks.spanHashesValid && checks.eventHashesValid && checks.sequenceValid;
1211
+ if (options.governance) {
1212
+ try {
1213
+ const govOpts = options.governance === true ? {} : options.governance;
1214
+ const summaries = await verifyGovernanceAttestations(bundle, govOpts);
1215
+ const failed = summaries.filter((s) => !s.verified);
1216
+ checks.governanceValid = failed.length === 0;
1217
+ for (const f of failed) {
1218
+ errors.push(
1219
+ `Governance attestation failed (${f.scheme}, role ${f.role}, attestor ${f.attestor})` + (f.error ? `: ${f.error}` : "")
1220
+ );
1221
+ }
1222
+ } catch (error) {
1223
+ checks.governanceValid = false;
1224
+ errors.push(
1225
+ `Failed to verify governance attestations: ${error instanceof Error ? error.message : String(error)}`
1226
+ );
1227
+ }
1228
+ }
1229
+ if (options.toolReceipts) {
1230
+ try {
1231
+ const outcome = await options.toolReceipts(bundle);
1232
+ checks.toolReceiptsValid = outcome.valid;
1233
+ if (!outcome.valid) {
1234
+ errors.push(...outcome.errors);
1235
+ }
1236
+ } catch (error) {
1237
+ checks.toolReceiptsValid = false;
1238
+ errors.push(
1239
+ `Failed to verify tool receipts: ${error instanceof Error ? error.message : String(error)}`
1240
+ );
1241
+ }
1242
+ }
1243
+ const valid = checks.rollingHashValid && checks.rootHashValid && checks.merkleRootValid && checks.spanHashesValid && checks.eventHashesValid && checks.sequenceValid && checks.modelManifestValid !== false && checks.governanceValid !== false && checks.toolReceiptsValid !== false;
691
1244
  return {
692
1245
  valid,
693
1246
  errors,
@@ -1284,8 +1837,10 @@ var VERSION = "0.1.0";
1284
1837
 
1285
1838
  exports.DEFAULT_EVENT_VISIBILITY = DEFAULT_EVENT_VISIBILITY;
1286
1839
  exports.HASH_DOMAIN_PREFIXES = HASH_DOMAIN_PREFIXES;
1840
+ exports.SS58_PREFIX = SS58_PREFIX;
1287
1841
  exports.VERSION = VERSION;
1288
1842
  exports.addEvent = addEvent;
1843
+ exports.addGovernanceAttestation = addGovernanceAttestation;
1289
1844
  exports.addSpan = addSpan;
1290
1845
  exports.buildSpanMerkleTree = buildSpanMerkleTree;
1291
1846
  exports.canDisclose = canDisclose;
@@ -1293,6 +1848,7 @@ exports.closeSpan = closeSpan;
1293
1848
  exports.computeEventHash = computeEventHash;
1294
1849
  exports.computeEventHashes = computeEventHashes;
1295
1850
  exports.computeManifestHash = computeManifestHash;
1851
+ exports.computeModelManifestHash = computeModelManifestHash;
1296
1852
  exports.computeRollingHash = computeRollingHash;
1297
1853
  exports.computeRootHash = computeRootHash;
1298
1854
  exports.computeSpanHash = computeSpanHash;
@@ -1300,11 +1856,15 @@ exports.countEventsByVisibility = countEventsByVisibility;
1300
1856
  exports.countSpansByVisibility = countSpansByVisibility;
1301
1857
  exports.createBundle = createBundle;
1302
1858
  exports.createDisclosureRequest = createDisclosureRequest;
1859
+ exports.createEd25519GovernanceSigner = createEd25519GovernanceSigner;
1860
+ exports.createEip712GovernanceVerifier = createEip712GovernanceVerifier;
1303
1861
  exports.createManifest = createManifest;
1862
+ exports.createSr25519GovernanceSigner = createSr25519GovernanceSigner;
1304
1863
  exports.createTrace = createTrace;
1305
1864
  exports.extractPublicView = extractPublicView;
1306
1865
  exports.filterPublicEvents = filterPublicEvents;
1307
1866
  exports.finalizeTrace = finalizeTrace;
1867
+ exports.freezeModelManifest = freezeModelManifest;
1308
1868
  exports.generateMerkleProof = generateMerkleProof;
1309
1869
  exports.getBundleSpanEvents = getSpanEvents2;
1310
1870
  exports.getChildSpans = getChildSpans;
@@ -1318,18 +1878,25 @@ exports.getSpan = getSpan;
1318
1878
  exports.getSpanCount = getSpanCount;
1319
1879
  exports.getSpanEvents = getSpanEvents;
1320
1880
  exports.getSpanIndex = getSpanIndex;
1881
+ exports.governanceAttestationPreimage = governanceAttestationPreimage;
1321
1882
  exports.initRollingHash = initRollingHash;
1322
1883
  exports.isFinalized = isFinalized;
1323
1884
  exports.isPublicEvent = isPublicEvent;
1324
1885
  exports.isPublicSpan = isPublicSpan;
1886
+ exports.manifestFromAnthropic = manifestFromAnthropic;
1887
+ exports.manifestFromCheckpoint = manifestFromCheckpoint;
1888
+ exports.manifestFromHuggingFace = manifestFromHuggingFace;
1889
+ exports.manifestFromOpenAI = manifestFromOpenAI;
1325
1890
  exports.parseChunkContent = parseChunkContent;
1326
1891
  exports.reconstructBundleFromManifest = reconstructBundleFromManifest;
1327
1892
  exports.selectiveDisclose = selectiveDisclose;
1328
1893
  exports.signBundle = signBundle;
1329
1894
  exports.updateRollingHash = updateRollingHash;
1895
+ exports.validateModelManifest = validateModelManifest;
1330
1896
  exports.verifyBundle = verifyBundle;
1331
1897
  exports.verifyBundleSignature = verifyBundleSignature;
1332
1898
  exports.verifyDisclosure = verifyDisclosure;
1899
+ exports.verifyGovernanceAttestations = verifyGovernanceAttestations;
1333
1900
  exports.verifyManifest = verifyManifest;
1334
1901
  exports.verifyMerkleProof = verifyMerkleProof;
1335
1902
  exports.verifyRollingHash = verifyRollingHash;