@fluxpointstudios/orynq-sdk-process-trace 0.2.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.d.ts CHANGED
@@ -105,10 +105,106 @@ interface CustomEvent extends BaseTraceEvent {
105
105
  eventType: string;
106
106
  data: Record<string, unknown>;
107
107
  }
108
+ /**
109
+ * Signature scheme used by a governance attestor.
110
+ * - "sr25519" / "ed25519": Substrate/Materios wallets (verified in-package)
111
+ * - "eip712": EVM typed-data signatures (verified via a pluggable verifier)
112
+ */
113
+ type GovernanceSignatureScheme = "sr25519" | "ed25519" | "eip712";
114
+ /**
115
+ * EIP-712 typed-data binding, required to verify an `eip712` governance
116
+ * signature. Mirrors the shape consumed by viem's `verifyTypedData`.
117
+ */
118
+ interface GovernanceEip712Binding {
119
+ domain: Record<string, unknown>;
120
+ types: Record<string, Array<{
121
+ name: string;
122
+ type: string;
123
+ }>>;
124
+ primaryType: string;
125
+ message?: Record<string, unknown>;
126
+ }
127
+ /**
128
+ * Governance attestation event — a verifiable, role-scoped sign-off recorded
129
+ * inside a trace (compliance review, release approval, data-steward sign-off).
130
+ *
131
+ * The signature is computed over a canonical, domain-separated preimage of
132
+ * `(runId || role || policyRef || decisionRef || signedAt)` (see
133
+ * `governanceAttestationPreimage`), so an auditor can verify *who* governed a
134
+ * decision without trusting the wrapper that recorded it, and a genuine
135
+ * attestation cannot be replayed into a different trace.
136
+ *
137
+ * Default visibility: "public" (governance provenance is meant to be auditable).
138
+ */
139
+ interface GovernanceAttestationEvent extends BaseTraceEvent {
140
+ kind: "governance-attestation";
141
+ /** Governance role; common values plus free-form extension. */
142
+ role: "compliance" | "release-authority" | "data-steward" | (string & {});
143
+ /** Hash or URI of the policy being attested to. */
144
+ policyRef: string;
145
+ /** Hash or id of the decision/event being governed. */
146
+ decisionRef: string;
147
+ /** The signing identity and scheme. */
148
+ attestor: {
149
+ address: string;
150
+ signatureScheme: GovernanceSignatureScheme;
151
+ };
152
+ /** Signature over the canonical preimage (hex, optionally `0x`-prefixed). */
153
+ signature: string;
154
+ /** ISO 8601 timestamp; part of the signed preimage. */
155
+ signedAt: string;
156
+ /** EIP-712 binding — required only when `attestor.signatureScheme === "eip712"`. */
157
+ eip712?: GovernanceEip712Binding;
158
+ }
159
+ /**
160
+ * Signature scheme for a tool-call receipt.
161
+ * - "http-message-signatures": RFC 9421 signed HTTP responses
162
+ * - "stripe-webhook" / "github-webhook": SaaS webhook HMAC signatures
163
+ * - "jws": generic JWS/JWT-signed responses
164
+ * - (string): forward-compatible custom schemes
165
+ */
166
+ type ToolReceiptScheme = "http-message-signatures" | "stripe-webhook" | "github-webhook" | "jws" | (string & {});
167
+ /**
168
+ * Verifiable tool-call receipt event — proves "the tool actually returned this
169
+ * response", not merely "the agent says the tool returned this response".
170
+ *
171
+ * The wrapper records a signed receipt produced by (or about) the external
172
+ * system; a verifier independently re-checks `receipt.signature` over
173
+ * `receipt.signedPayload` against `receipt.signer`.
174
+ *
175
+ * Default visibility: "private" (responses may contain PII / secrets — only the
176
+ * hashes and signature are needed for verification).
177
+ */
178
+ interface ToolReceiptEvent extends BaseTraceEvent {
179
+ kind: "tool-receipt";
180
+ /** Identifier of the tool/endpoint that was called. */
181
+ toolId: string;
182
+ /** Commitment to the request (SHA-256 hex). */
183
+ request: {
184
+ hash: string;
185
+ };
186
+ /** Commitment to the response, with an optional retained payload. */
187
+ response: {
188
+ hash: string;
189
+ payload?: unknown;
190
+ };
191
+ /** The independently-verifiable signed receipt. */
192
+ receipt: {
193
+ scheme: ToolReceiptScheme;
194
+ /** Verifier-resolvable identity: URL, DID, on-chain address, or keyId. */
195
+ signer: string;
196
+ /** Signature bytes (encoding depends on scheme: base64/hex/0x-hex). */
197
+ signature: string;
198
+ /** Canonicalized signed bytes the signature is computed over. */
199
+ signedPayload: string;
200
+ /** Scheme-specific verification material (headers, keyId, components, ...). */
201
+ params?: Record<string, unknown>;
202
+ };
203
+ }
108
204
  /**
109
205
  * Discriminated union of all trace event types.
110
206
  */
111
- type TraceEvent = CommandEvent | OutputEvent | DecisionEvent | ObservationEvent | ErrorTraceEvent | CustomEvent;
207
+ type TraceEvent = CommandEvent | OutputEvent | DecisionEvent | ObservationEvent | ErrorTraceEvent | CustomEvent | GovernanceAttestationEvent | ToolReceiptEvent;
112
208
  /**
113
209
  * Event kind string literals for type guards.
114
210
  */
@@ -146,6 +242,36 @@ interface TraceSpan {
146
242
  metadata?: Record<string, unknown>;
147
243
  hash?: string;
148
244
  }
245
+ /**
246
+ * Fingerprint of the model/data state used during a trace run.
247
+ *
248
+ * Distinct from {@link TraceManifest} (which describes off-chain *storage*
249
+ * chunks). A `ModelManifest` is pinned at {@link CreateTraceOptions} time —
250
+ * *before* execution — and frozen, so the resulting trace can prove that
251
+ * "neither the data nor the model was altered" for a given inference.
252
+ *
253
+ * Two traces of "the same model" should produce the same `modelManifestHash`,
254
+ * so the field values must be deterministic fingerprints (see the
255
+ * `manifestFrom*` builders).
256
+ */
257
+ interface ModelManifest {
258
+ /** Model checkpoint fingerprint, e.g. "sha256:..." */
259
+ modelHash: string;
260
+ /** Tokenizer fingerprint. */
261
+ tokenizerHash?: string;
262
+ /** System-prompt fingerprint. */
263
+ systemPromptHash?: string;
264
+ /** Training-dataset manifest fingerprint. */
265
+ trainingDataManifest?: string;
266
+ /** Producing framework, e.g. "huggingface" | "openai" | "anthropic" | "checkpoint". */
267
+ framework?: string;
268
+ /** Model identifier (e.g. HF repo id, OpenAI/Anthropic model name). */
269
+ modelId?: string;
270
+ /** Revision / snapshot id, when applicable. */
271
+ revision?: string;
272
+ /** Free-form additional fingerprint inputs (hashed into manifestHash). */
273
+ metadata?: Record<string, unknown>;
274
+ }
149
275
  /**
150
276
  * Complete trace run containing all events and spans.
151
277
  *
@@ -174,6 +300,18 @@ interface TraceRun {
174
300
  rootHash?: string;
175
301
  nextSeq: number;
176
302
  nextSpanSeq: number;
303
+ /**
304
+ * Model/data manifest pinned at createTrace() time (frozen). When present,
305
+ * `modelManifestHash` is the cryptographic commitment to it.
306
+ */
307
+ modelManifest?: ModelManifest;
308
+ /** H("poi-trace:model-manifest:v1|" + canonical(modelManifest)), pinned at creation. */
309
+ modelManifestHash?: string;
310
+ /**
311
+ * Strict-mode flag (pinned at creation). When true, finalizeTrace() throws
312
+ * if no manifest was pinned. Default false (warn-only) for v0.x.
313
+ */
314
+ strict?: boolean;
177
315
  }
178
316
  /**
179
317
  * State for incremental rolling hash computation.
@@ -246,6 +384,10 @@ interface TraceBundlePublicView {
246
384
  }>;
247
385
  redactionPolicyId?: string;
248
386
  redactionRulesHash?: string;
387
+ /** Model-state commitment (public-safe: it is only a hash). */
388
+ modelManifestHash?: string;
389
+ /** Pinned model manifest (hashes only — public-safe). */
390
+ modelManifest?: ModelManifest;
249
391
  }
250
392
  /**
251
393
  * Complete trace bundle with cryptographic commitments.
@@ -267,6 +409,14 @@ interface TraceBundle {
267
409
  merkleRoot: string;
268
410
  rootHash: string;
269
411
  manifestHash?: string;
412
+ /**
413
+ * Model/data manifest commitment pinned at createTrace() time. Distinct from
414
+ * `manifestHash` (the off-chain storage-manifest hash). Place this in on-chain
415
+ * anchor metadata to make model drift cryptographically detectable.
416
+ */
417
+ modelManifestHash?: string;
418
+ /** The pinned model manifest (hashes only — public-safe). */
419
+ modelManifest?: ModelManifest;
270
420
  signerId?: string;
271
421
  signature?: string;
272
422
  }
@@ -366,6 +516,22 @@ interface TraceVerificationResult {
366
516
  spanHashesValid: boolean;
367
517
  eventHashesValid: boolean;
368
518
  sequenceValid: boolean;
519
+ /**
520
+ * Model-manifest pin binding (#59): the pinned manifest hashes to its
521
+ * recorded commitment AND that commitment is folded into the committed
522
+ * root. True when no manifest is pinned (nothing to bind).
523
+ */
524
+ modelManifestValid?: boolean;
525
+ /**
526
+ * Set only when governance verification is requested via
527
+ * verifyBundle(bundle, { governance }). Undefined means "not checked".
528
+ */
529
+ governanceValid?: boolean;
530
+ /**
531
+ * Set only when tool-receipt verification is requested via
532
+ * verifyBundle(bundle, { toolReceipts }). Undefined means "not checked".
533
+ */
534
+ toolReceiptsValid?: boolean;
369
535
  };
370
536
  }
371
537
  /**
@@ -389,6 +555,17 @@ interface CreateTraceOptions {
389
555
  agentId: string;
390
556
  description?: string;
391
557
  metadata?: Record<string, unknown>;
558
+ /**
559
+ * Model/data manifest to pin *before* execution. Its hash is computed and
560
+ * frozen at createTrace() time; mutating the manifest afterwards throws.
561
+ */
562
+ manifest?: ModelManifest;
563
+ /**
564
+ * Strict mode. When true, createTrace() requires a `manifest` and
565
+ * finalizeTrace() refuses to finalize an unpinned trace. Default false
566
+ * (warn-only) for v0.x; planned strict-by-default in v1.0.
567
+ */
568
+ strict?: boolean;
392
569
  }
393
570
  /**
394
571
  * Options for creating a new span.
@@ -418,6 +595,10 @@ declare const HASH_DOMAIN_PREFIXES: {
418
595
  readonly node: "poi-trace:node:v1|";
419
596
  readonly manifest: "poi-trace:manifest:v1|";
420
597
  readonly root: "poi-trace:root:v1|";
598
+ /** Model/data manifest commitment (pre-execution pinning). */
599
+ readonly modelManifest: "poi-trace:model-manifest:v1|";
600
+ /** Governance-attestation signing preimage. */
601
+ readonly governance: "poi-trace:governance:v1|";
421
602
  };
422
603
  /**
423
604
  * Type for domain prefix keys.
@@ -1127,28 +1308,31 @@ declare function computeRollingHash(events: TraceEvent[]): Promise<string>;
1127
1308
  */
1128
1309
  declare function verifyRollingHash(events: TraceEvent[], expectedHash: string): Promise<boolean>;
1129
1310
  /**
1130
- * Compute the final root hash from rolling hash and span hashes.
1311
+ * Compute the final root hash from rolling hash, span hashes, and (when pinned)
1312
+ * the model-manifest commitment.
1131
1313
  *
1132
1314
  * The root hash is computed as:
1133
- * `H("poi-trace:root:v1|" + rollingHash + "|" + spanHash1 + "|" + spanHash2 + ...)`
1315
+ * `H("poi-trace:root:v1|" + rollingHash + "|" + spanHash1 + "|" + ... [+ "|manifest:" + modelManifestHash])`
1134
1316
  *
1135
1317
  * Spans are sorted by their `spanSeq` field before joining to ensure
1136
- * deterministic ordering. This creates a single commitment that covers
1137
- * both the event sequence (via rolling hash) and the span structure.
1318
+ * deterministic ordering. Folding `modelManifestHash` in binds the pinned
1319
+ * model/data manifest to the committed root (issue #59): swapping the manifest
1320
+ * after commitment changes the recomputed root and fails verification.
1138
1321
  *
1139
1322
  * @param rollingHash - The final rolling hash from all events
1140
1323
  * @param spans - Array of trace spans (must have hash field populated)
1324
+ * @param modelManifestHash - Optional pre-execution model-manifest commitment to bind
1141
1325
  * @returns Promise resolving to the root hash as a hex string
1142
1326
  *
1143
1327
  * @example
1144
1328
  * ```typescript
1145
1329
  * const rollingHash = await computeRollingHash(events);
1146
- * const rootHash = await computeRootHash(rollingHash, spans);
1330
+ * const rootHash = await computeRootHash(rollingHash, spans, run.modelManifestHash);
1147
1331
  *
1148
1332
  * // rootHash can now be published as the trace commitment
1149
1333
  * ```
1150
1334
  */
1151
- declare function computeRootHash(rollingHash: string, spans: TraceSpan[]): Promise<string>;
1335
+ declare function computeRootHash(rollingHash: string, spans: TraceSpan[], modelManifestHash?: string): Promise<string>;
1152
1336
  /**
1153
1337
  * Compute event hashes for multiple events in batch.
1154
1338
  * Useful for pre-computing hashes before building a Merkle tree.
@@ -1273,6 +1457,262 @@ declare function verifyMerkleProof(proof: MerkleProof): Promise<boolean>;
1273
1457
  */
1274
1458
  declare function verifySpanInclusion(proof: MerkleProof, span: TraceSpan, events: TraceEvent[]): Promise<boolean>;
1275
1459
 
1460
+ /**
1461
+ * @fileoverview Governance attestations (issue #58).
1462
+ *
1463
+ * Location: packages/process-trace/src/governance.ts
1464
+ *
1465
+ * A `governance-attestation` event records a verifiable, role-scoped sign-off
1466
+ * inside a trace (compliance review, release approval, data-steward sign-off),
1467
+ * so an auditor can answer "who governed this decision?" without trusting the
1468
+ * wrapper that recorded it.
1469
+ *
1470
+ * Signing model:
1471
+ * - The signature covers a canonical, domain-separated preimage of
1472
+ * `(role || policyRef || decisionRef || signedAt)` — see
1473
+ * {@link governanceAttestationPreimage}.
1474
+ * - `sr25519` and `ed25519` are verified in-package via the OPTIONAL peer
1475
+ * dependencies `@polkadot/util-crypto` + `@polkadot/util` (loaded with a
1476
+ * dynamic `import()` so base installs stay lean).
1477
+ * - `eip712` is verified via a pluggable {@link GovernanceVerifier} so this
1478
+ * package never needs a hard dependency on viem. See
1479
+ * {@link createEip712GovernanceVerifier}.
1480
+ *
1481
+ * @example
1482
+ * ```typescript
1483
+ * const signer = await createSr25519GovernanceSigner({ seed: "0x" + "11".repeat(32) });
1484
+ * await addGovernanceAttestation(run, span.id, {
1485
+ * role: "compliance",
1486
+ * policyRef: "sha256:...",
1487
+ * decisionRef: eventId,
1488
+ * signer,
1489
+ * });
1490
+ * // ...later, during audit: the attestor identity comes from the untrusted
1491
+ * // trace, so the caller MUST allow-list the authorized signer(s) — a valid
1492
+ * // self-signed attestation from an arbitrary key is not a real sign-off.
1493
+ * const summary = await verifyGovernanceAttestations(bundle, {
1494
+ * authorizedAttestors: [signer.address],
1495
+ * });
1496
+ * // [{ role: "compliance", attestor: "5...", scheme: "sr25519", verified: true, authorized: true }]
1497
+ * ```
1498
+ */
1499
+
1500
+ /** The fields that are bound by a governance signature. */
1501
+ interface GovernanceAttestationFields {
1502
+ role: string;
1503
+ policyRef: string;
1504
+ decisionRef: string;
1505
+ signedAt: string;
1506
+ /**
1507
+ * Trace run id this attestation is scoped to. Binding it prevents replaying a
1508
+ * genuine attestation from trace X into an unrelated trace Y (#58).
1509
+ */
1510
+ runId: string;
1511
+ }
1512
+ /**
1513
+ * Build the canonical, domain-separated preimage signed by sr25519/ed25519
1514
+ * governance signers. Deterministic — a verifier reconstructs identical bytes
1515
+ * from the recorded event fields plus the enclosing run id.
1516
+ *
1517
+ * Each field is LENGTH-PREFIXED (4-byte big-endian byte length) rather than
1518
+ * delimiter-joined: a raw `\n` separator let two different tuples produce
1519
+ * identical bytes when a field value (role/policyRef/decisionRef are
1520
+ * attacker-supplied) itself contained `\n`, so one signature re-sliced to bind
1521
+ * a different claim. Length-prefixing makes field boundaries unambiguous
1522
+ * regardless of the field contents.
1523
+ */
1524
+ declare function governanceAttestationPreimage(fields: GovernanceAttestationFields): Uint8Array;
1525
+ /** Context passed to a {@link GovernanceSigner}. */
1526
+ interface GovernanceSignContext {
1527
+ /** Canonical preimage bytes (sr25519/ed25519 signers sign these). */
1528
+ preimage: Uint8Array;
1529
+ /** The raw fields, for signers (e.g. eip712) that build their own payload. */
1530
+ fields: GovernanceAttestationFields;
1531
+ /** Present iff the caller supplied an eip712 binding. */
1532
+ eip712?: GovernanceEip712Binding;
1533
+ }
1534
+ /**
1535
+ * A governance signer. Consumers provide an implementation (HSM, KMS, wallet,
1536
+ * or one of the built-in {@link createSr25519GovernanceSigner} /
1537
+ * {@link createEd25519GovernanceSigner} factories).
1538
+ */
1539
+ interface GovernanceSigner {
1540
+ /** Verifier-resolvable identity (SS58 address for substrate, 0x-address for evm). */
1541
+ address: string;
1542
+ signatureScheme: GovernanceSignatureScheme;
1543
+ /** Return the signature as a hex string (with or without `0x`). */
1544
+ sign(ctx: GovernanceSignContext): Promise<string> | string;
1545
+ }
1546
+ interface AddGovernanceAttestationOptions {
1547
+ role: GovernanceAttestationEvent["role"];
1548
+ policyRef: string;
1549
+ decisionRef: string;
1550
+ signer: GovernanceSigner;
1551
+ /** ISO 8601 timestamp; defaults to now. Included in the signed preimage. */
1552
+ signedAt?: string;
1553
+ /** Event visibility; defaults to "public" (governance is auditable). */
1554
+ visibility?: Visibility;
1555
+ /** EIP-712 binding — required when the signer scheme is "eip712". */
1556
+ eip712?: GovernanceEip712Binding;
1557
+ }
1558
+ /**
1559
+ * Sign and append a `governance-attestation` event to a span.
1560
+ *
1561
+ * @returns the recorded {@link GovernanceAttestationEvent} (with runtime fields).
1562
+ */
1563
+ declare function addGovernanceAttestation(run: TraceRun, spanId: string, opts: AddGovernanceAttestationOptions): Promise<GovernanceAttestationEvent>;
1564
+ /** Default SS58 prefix used across the Orynq/Materios ecosystem. */
1565
+ declare const SS58_PREFIX = 42;
1566
+ interface SubstrateGovernanceSignerOptions {
1567
+ /** 32-byte seed as bytes or 0x-hex. Provide this OR (secretKey + publicKey). */
1568
+ seed?: Uint8Array | string;
1569
+ /** Expanded secret key (with publicKey). */
1570
+ secretKey?: Uint8Array;
1571
+ publicKey?: Uint8Array;
1572
+ /** Override the derived SS58 address. */
1573
+ address?: string;
1574
+ /** SS58 format for the derived address (default 42). */
1575
+ ss58Format?: number;
1576
+ }
1577
+ /**
1578
+ * Create an sr25519 governance signer backed by `@polkadot/util-crypto`.
1579
+ * Pass a 32-byte `seed` (bytes or 0x-hex) or an explicit `secretKey`+`publicKey`.
1580
+ */
1581
+ declare function createSr25519GovernanceSigner(opts: SubstrateGovernanceSignerOptions): Promise<GovernanceSigner>;
1582
+ /**
1583
+ * Create an ed25519 governance signer backed by `@polkadot/util-crypto`.
1584
+ * Pass a 32-byte `seed` (bytes or 0x-hex) or an explicit `secretKey`+`publicKey`.
1585
+ */
1586
+ declare function createEd25519GovernanceSigner(opts: SubstrateGovernanceSignerOptions): Promise<GovernanceSigner>;
1587
+ /** Context passed to a {@link GovernanceVerifier} alongside the event. */
1588
+ interface GovernanceVerifyContext {
1589
+ /** Canonical preimage bytes bound to this trace's run id (#58). */
1590
+ preimage: Uint8Array;
1591
+ /** The enclosing trace run id — verifiers MUST bind signatures to it. */
1592
+ runId: string;
1593
+ }
1594
+ /** A pluggable verifier for a single governance signature scheme. */
1595
+ type GovernanceVerifier = (event: GovernanceAttestationEvent, context: GovernanceVerifyContext) => Promise<boolean> | boolean;
1596
+ interface VerifyGovernanceOptions {
1597
+ /**
1598
+ * Per-scheme verifier overrides. An `eip712` verifier MUST be supplied here
1599
+ * (e.g. via {@link createEip712GovernanceVerifier}); sr25519/ed25519 fall back
1600
+ * to the built-in @polkadot verifiers when not overridden.
1601
+ */
1602
+ verifiers?: Partial<Record<GovernanceSignatureScheme, GovernanceVerifier>>;
1603
+ /**
1604
+ * The set of attestor identities (SS58 / 0x-address, case-insensitive) that
1605
+ * are authorized to sign governance attestations. The attestor identity comes
1606
+ * from the untrusted trace, so a cryptographically valid self-signed
1607
+ * attestation from an arbitrary key is NOT a real sign-off — only a key on
1608
+ * this list counts. When OMITTED, governance verification FAILS CLOSED: every
1609
+ * attestation is `authorized: false` / `verified: false`, so an
1610
+ * "anyone can sign" attestation can never fold into a passing bundle verdict.
1611
+ * Optionally scope keys to a role via {@link authorizedAttestorsByRole}.
1612
+ */
1613
+ authorizedAttestors?: string[];
1614
+ /**
1615
+ * Per-role authorized attestors (case-insensitive). When present for an
1616
+ * attestation's role, the signer must be listed under THAT role — a
1617
+ * data-steward key cannot pass off a release-authority sign-off. Falls back to
1618
+ * {@link authorizedAttestors} for roles not present here.
1619
+ */
1620
+ authorizedAttestorsByRole?: Record<string, string[]>;
1621
+ }
1622
+ /** Per-attestation verification result. */
1623
+ interface GovernanceAttestationSummary {
1624
+ eventId: string;
1625
+ role: string;
1626
+ attestor: string;
1627
+ scheme: GovernanceSignatureScheme;
1628
+ policyRef: string;
1629
+ decisionRef: string;
1630
+ /** The signature cryptographically verifies AND the signer is authorized. */
1631
+ verified: boolean;
1632
+ /** The attestor is on the caller-supplied authorized-signer allow-list. */
1633
+ authorized: boolean;
1634
+ error?: string;
1635
+ }
1636
+ /**
1637
+ * Verify every `governance-attestation` event in a bundle and return a summary
1638
+ * tuple per attestation. Auditors get governance provenance "for free" — this
1639
+ * is also invoked by `verifyBundle(bundle, { governance: true })`.
1640
+ */
1641
+ declare function verifyGovernanceAttestations(bundle: TraceBundle, opts?: VerifyGovernanceOptions): Promise<GovernanceAttestationSummary[]>;
1642
+ /**
1643
+ * Build an `eip712` {@link GovernanceVerifier} from an injected
1644
+ * `verifyTypedData` (e.g. viem's). Keeps viem out of this package's deps.
1645
+ *
1646
+ * The schema pins (`expectedDomain`/`expectedPrimaryType`/`expectedTypes`) are
1647
+ * MANDATORY: the event's `eip712.{domain,primaryType,types}` are attacker-
1648
+ * controlled, so without pins an attacker signs an EMPTY struct
1649
+ * (`types:{Attestation:[]}`) with their own key and smuggles the claim fields as
1650
+ * untyped message extras the signature never commits to. The pinned primaryType
1651
+ * must also declare `role`, `policyRef`, `decisionRef`, `runId`, and `signedAt`
1652
+ * so the signature provably binds them. `signedAt` is then cross-checked against
1653
+ * the recorded event and held to a freshness window so a signed sign-off cannot
1654
+ * be replayed or backdated.
1655
+ *
1656
+ * @example
1657
+ * ```typescript
1658
+ * import { verifyTypedData } from "viem";
1659
+ * const summary = await verifyGovernanceAttestations(bundle, {
1660
+ * verifiers: {
1661
+ * eip712: createEip712GovernanceVerifier({
1662
+ * verifyTypedData,
1663
+ * expectedDomain: { name: "Orynq", version: "1" },
1664
+ * expectedPrimaryType: "Attestation",
1665
+ * expectedTypes: {
1666
+ * Attestation: [
1667
+ * { name: "role", type: "string" },
1668
+ * { name: "policyRef", type: "string" },
1669
+ * { name: "decisionRef", type: "string" },
1670
+ * { name: "runId", type: "string" },
1671
+ * { name: "signedAt", type: "string" },
1672
+ * ],
1673
+ * },
1674
+ * }),
1675
+ * },
1676
+ * authorizedAttestors: ["0x<release-authority>"],
1677
+ * });
1678
+ * ```
1679
+ */
1680
+ declare function createEip712GovernanceVerifier(deps: {
1681
+ verifyTypedData: (args: {
1682
+ address: `0x${string}`;
1683
+ domain: Record<string, unknown>;
1684
+ types: Record<string, Array<{
1685
+ name: string;
1686
+ type: string;
1687
+ }>>;
1688
+ primaryType: string;
1689
+ message: Record<string, unknown>;
1690
+ signature: `0x${string}`;
1691
+ }) => Promise<boolean> | boolean;
1692
+ /**
1693
+ * Expected EIP-712 domain (name/version/chainId/verifyingContract). The
1694
+ * event's `eip712.domain` is attacker-controlled, so the verifier requires an
1695
+ * EXACT match on every field — a swapped verifyingContract/chainId/name is
1696
+ * rejected before the signature is trusted.
1697
+ */
1698
+ expectedDomain: Record<string, unknown>;
1699
+ /** Expected `primaryType`; a mismatch is rejected. */
1700
+ expectedPrimaryType: string;
1701
+ /** Expected `types` map; the event's must deep-equal it. */
1702
+ expectedTypes: Record<string, Array<{
1703
+ name: string;
1704
+ type: string;
1705
+ }>>;
1706
+ /**
1707
+ * Max age of an attestation, in ms, before it is rejected as stale — measured
1708
+ * from `signedAt` to `nowMs`. Also rejects far-future timestamps beyond the
1709
+ * same window (clock-skew tolerance). Defaults to {@link DEFAULT_EIP712_FRESHNESS_MS}.
1710
+ */
1711
+ freshnessToleranceMs?: number;
1712
+ /** Epoch-ms clock override (testing). Defaults to `Date.now()`. */
1713
+ nowMs?: number;
1714
+ }): GovernanceVerifier;
1715
+
1276
1716
  /**
1277
1717
  * @fileoverview Bundle creation, extraction, verification, and signing for trace bundles.
1278
1718
  *
@@ -1320,6 +1760,31 @@ declare function verifySpanInclusion(proof: MerkleProof, span: TraceSpan, events
1320
1760
  * ```
1321
1761
  */
1322
1762
 
1763
+ /**
1764
+ * Outcome shape returned by an injected tool-receipt verifier (provided by
1765
+ * `@fluxpointstudios/orynq-sdk-tool-receipts` — passed in to avoid a circular
1766
+ * dependency on that package from process-trace).
1767
+ */
1768
+ interface ToolReceiptVerifyOutcome {
1769
+ valid: boolean;
1770
+ errors: string[];
1771
+ }
1772
+ /** Options for {@link verifyBundle}. */
1773
+ interface VerifyBundleOptions {
1774
+ /**
1775
+ * Verify `governance-attestation` events. `true` uses the built-in
1776
+ * sr25519/ed25519 verifiers; pass {@link VerifyGovernanceOptions} to register
1777
+ * a pluggable verifier (e.g. eip712). Any unverified attestation fails the
1778
+ * bundle.
1779
+ */
1780
+ governance?: boolean | VerifyGovernanceOptions;
1781
+ /**
1782
+ * Verify `tool-receipt` events with an injected verifier from
1783
+ * `@fluxpointstudios/orynq-sdk-tool-receipts`. Any failed receipt fails the
1784
+ * bundle.
1785
+ */
1786
+ toolReceipts?: (bundle: TraceBundle) => Promise<ToolReceiptVerifyOutcome> | ToolReceiptVerifyOutcome;
1787
+ }
1323
1788
  /**
1324
1789
  * Check if a span should be included in public view.
1325
1790
  *
@@ -1448,7 +1913,7 @@ declare function extractPublicView(bundle: TraceBundle): TraceBundlePublicView;
1448
1913
  * }
1449
1914
  * ```
1450
1915
  */
1451
- declare function verifyBundle(bundle: TraceBundle): Promise<TraceVerificationResult>;
1916
+ declare function verifyBundle(bundle: TraceBundle, options?: VerifyBundleOptions): Promise<TraceVerificationResult>;
1452
1917
  /**
1453
1918
  * Sign a bundle using the provided signature provider.
1454
1919
  *
@@ -1715,6 +2180,105 @@ declare function parseChunkContent(content: string): {
1715
2180
  events: TraceEvent[];
1716
2181
  };
1717
2182
 
2183
+ /**
2184
+ * @fileoverview Pre-execution model/data manifest pinning (issue #59).
2185
+ *
2186
+ * Location: packages/process-trace/src/model-manifest.ts
2187
+ *
2188
+ * A {@link ModelManifest} fingerprints the model/data state used for an
2189
+ * inference. It is pinned at `createTrace()` time — *before* execution — and
2190
+ * frozen, so the finalized trace can prove that "neither the data nor the model
2191
+ * was altered" for that run. This is distinct from the off-chain *storage*
2192
+ * manifest in `manifest.ts` ({@link TraceManifest}).
2193
+ *
2194
+ * The `manifestFrom*` builders compute a model's fingerprint deterministically
2195
+ * so two traces of "the same model" produce the same `modelManifestHash`.
2196
+ *
2197
+ * @example
2198
+ * ```typescript
2199
+ * const manifest = await manifestFromHuggingFace({
2200
+ * modelId: "meta-llama/Llama-3.1-8B",
2201
+ * revision: "0e9e39f249a16976918f6564b8830bc894c89659",
2202
+ * });
2203
+ * const run = await createTrace({ agentId: "agent-1", manifest, strict: true });
2204
+ * ```
2205
+ */
2206
+
2207
+ /**
2208
+ * Compute the cryptographic commitment to a model manifest:
2209
+ * `H("poi-trace:model-manifest:v1|" + canonical(manifest))`.
2210
+ *
2211
+ * Deterministic: the same manifest fields always produce the same hash, so two
2212
+ * traces pinning the same model state share a `modelManifestHash`.
2213
+ */
2214
+ declare function computeModelManifestHash(manifest: ModelManifest): Promise<string>;
2215
+ /**
2216
+ * Validate the shape of a model manifest. Throws on the only hard requirement —
2217
+ * a non-empty `modelHash` — and otherwise returns the manifest unchanged.
2218
+ */
2219
+ declare function validateModelManifest(manifest: ModelManifest): ModelManifest;
2220
+ /**
2221
+ * Deep-freeze a model manifest so that mutating it after pinning throws (in ESM
2222
+ * strict mode). Returns the same object, frozen.
2223
+ */
2224
+ declare function freezeModelManifest(manifest: ModelManifest): Readonly<ModelManifest>;
2225
+ /** Common optional inputs shared across the API-model builders. */
2226
+ interface CommonManifestInputs {
2227
+ tokenizerHash?: string;
2228
+ systemPrompt?: string;
2229
+ systemPromptHash?: string;
2230
+ trainingDataManifest?: string;
2231
+ metadata?: Record<string, unknown>;
2232
+ }
2233
+ interface HuggingFaceManifestOptions extends CommonManifestInputs {
2234
+ /** Hugging Face repo id, e.g. "meta-llama/Llama-3.1-8B". */
2235
+ modelId: string;
2236
+ /** Commit sha / branch / tag. A 40-hex commit sha gives the strongest guarantee. */
2237
+ revision?: string;
2238
+ }
2239
+ /**
2240
+ * Build a manifest for a Hugging Face model. The fingerprint binds the repo id
2241
+ * and revision; pass a commit sha as `revision` for an immutable pin.
2242
+ */
2243
+ declare function manifestFromHuggingFace(opts: HuggingFaceManifestOptions): Promise<ModelManifest>;
2244
+ interface OpenAIManifestOptions extends CommonManifestInputs {
2245
+ /** Model name, e.g. "gpt-4o". */
2246
+ model: string;
2247
+ /** Dated snapshot id, e.g. "gpt-4o-2024-08-06". Strongly recommended for pinning. */
2248
+ snapshotId?: string;
2249
+ }
2250
+ /**
2251
+ * Build a manifest for an OpenAI model. Pass a dated `snapshotId` (not just the
2252
+ * floating alias) so the pin is immutable.
2253
+ */
2254
+ declare function manifestFromOpenAI(opts: OpenAIManifestOptions): Promise<ModelManifest>;
2255
+ interface AnthropicManifestOptions extends CommonManifestInputs {
2256
+ /** Model name, e.g. "claude-opus-4-8". */
2257
+ model: string;
2258
+ /** Dated snapshot id, e.g. "claude-3-5-sonnet-20241022". Strongly recommended. */
2259
+ snapshotId?: string;
2260
+ }
2261
+ /**
2262
+ * Build a manifest for an Anthropic model. Pass a dated `snapshotId` so the pin
2263
+ * is immutable.
2264
+ */
2265
+ declare function manifestFromAnthropic(opts: AnthropicManifestOptions): Promise<ModelManifest>;
2266
+ interface CheckpointManifestOptions extends CommonManifestInputs {
2267
+ /** Optional override for the recorded model id (defaults to the file path). */
2268
+ modelId?: string;
2269
+ /** Optional revision/label for the checkpoint. */
2270
+ revision?: string;
2271
+ }
2272
+ /**
2273
+ * Build a manifest for a local checkpoint file by hashing its bytes. The
2274
+ * `modelHash` is `sha256:<sha256(file bytes)>`, so any change to the checkpoint
2275
+ * changes the manifest.
2276
+ *
2277
+ * Node-only: dynamically imports `node:fs/promises` so the module stays
2278
+ * bundler-safe for non-Node consumers that never call this builder.
2279
+ */
2280
+ declare function manifestFromCheckpoint(filepath: string, opts?: CheckpointManifestOptions): Promise<ModelManifest>;
2281
+
1718
2282
  /**
1719
2283
  * @summary Main entry point for @fluxpointstudios/orynq-sdk-process-trace package.
1720
2284
  *
@@ -1755,4 +2319,4 @@ declare function parseChunkContent(content: string): {
1755
2319
  */
1756
2320
  declare const VERSION = "0.1.0";
1757
2321
 
1758
- export { type AnnotatedSpan, type BaseTraceEvent, type Chunk, type ChunkInfo, type CommandEvent, type CreateManifestOptions, type CreateSpanOptions, type CreateTraceOptions, type CustomEvent, DEFAULT_EVENT_VISIBILITY, type DecisionEvent, type DisclosureMode, type DisclosureRequest, type DisclosureResult, type ErrorTraceEvent, HASH_DOMAIN_PREFIXES, type HashDomain, type ManifestVerificationResult, type MerkleProof, type ObservationEvent, type OutputEvent, type RollingHashState, type SchemaVersion, type SignatureProvider, type TraceBundle, type TraceBundlePublicView, type TraceEvent, type TraceEventKind, type TraceManifest, type TraceMerkleTree, type TraceRun, type TraceSpan, type TraceStatus, type TraceVerificationResult, VERSION, type Visibility, addEvent, addSpan, buildSpanMerkleTree, canDisclose, closeSpan, computeEventHash, computeEventHashes, computeManifestHash, computeRollingHash, computeRootHash, computeSpanHash, countEventsByVisibility, countSpansByVisibility, createBundle, createDisclosureRequest, createManifest, createTrace, extractPublicView, filterPublicEvents, finalizeTrace, generateMerkleProof, getSpanEvents as getBundleSpanEvents, getChildSpans, getChunkPath, getEvent, getEventCount, getEventsByKind, getGenesisHash, getRootSpans, getSpan, getSpanCount, getSpanEvents$1 as getSpanEvents, getSpanIndex, initRollingHash, isFinalized, isPublicEvent, isPublicSpan, parseChunkContent, reconstructBundleFromManifest, selectiveDisclose, signBundle, updateRollingHash, verifyBundle, verifyBundleSignature, verifyDisclosure, verifyManifest, verifyMerkleProof, verifyRollingHash, verifySpanDisclosure, verifySpanInclusion };
2322
+ export { type AddGovernanceAttestationOptions, type AnnotatedSpan, type AnthropicManifestOptions, type BaseTraceEvent, type CheckpointManifestOptions, type Chunk, type ChunkInfo, type CommandEvent, type CreateManifestOptions, type CreateSpanOptions, type CreateTraceOptions, type CustomEvent, DEFAULT_EVENT_VISIBILITY, type DecisionEvent, type DisclosureMode, type DisclosureRequest, type DisclosureResult, type ErrorTraceEvent, type GovernanceAttestationEvent, type GovernanceAttestationFields, type GovernanceAttestationSummary, type GovernanceEip712Binding, type GovernanceSignContext, type GovernanceSignatureScheme, type GovernanceSigner, type GovernanceVerifier, type GovernanceVerifyContext, HASH_DOMAIN_PREFIXES, type HashDomain, type HuggingFaceManifestOptions, type ManifestVerificationResult, type MerkleProof, type ModelManifest, type ObservationEvent, type OpenAIManifestOptions, type OutputEvent, type RollingHashState, SS58_PREFIX, type SchemaVersion, type SignatureProvider, type SubstrateGovernanceSignerOptions, type ToolReceiptEvent, type ToolReceiptScheme, type ToolReceiptVerifyOutcome, type TraceBundle, type TraceBundlePublicView, type TraceEvent, type TraceEventKind, type TraceManifest, type TraceMerkleTree, type TraceRun, type TraceSpan, type TraceStatus, type TraceVerificationResult, VERSION, type VerifyBundleOptions, type VerifyGovernanceOptions, type Visibility, addEvent, addGovernanceAttestation, addSpan, buildSpanMerkleTree, canDisclose, closeSpan, computeEventHash, computeEventHashes, computeManifestHash, computeModelManifestHash, computeRollingHash, computeRootHash, computeSpanHash, countEventsByVisibility, countSpansByVisibility, createBundle, createDisclosureRequest, createEd25519GovernanceSigner, createEip712GovernanceVerifier, createManifest, createSr25519GovernanceSigner, createTrace, extractPublicView, filterPublicEvents, finalizeTrace, freezeModelManifest, generateMerkleProof, getSpanEvents as getBundleSpanEvents, getChildSpans, getChunkPath, getEvent, getEventCount, getEventsByKind, getGenesisHash, getRootSpans, getSpan, getSpanCount, getSpanEvents$1 as getSpanEvents, getSpanIndex, governanceAttestationPreimage, initRollingHash, isFinalized, isPublicEvent, isPublicSpan, manifestFromAnthropic, manifestFromCheckpoint, manifestFromHuggingFace, manifestFromOpenAI, parseChunkContent, reconstructBundleFromManifest, selectiveDisclose, signBundle, updateRollingHash, validateModelManifest, verifyBundle, verifyBundleSignature, verifyDisclosure, verifyGovernanceAttestations, verifyManifest, verifyMerkleProof, verifyRollingHash, verifySpanDisclosure, verifySpanInclusion };