@sanning/proof 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -53,7 +53,7 @@ contentHashes(env); // all content hashes an envelope commits to, by event typ
53
53
 
54
54
  ## Verify a whole trace bundle: one command
55
55
 
56
- The `@sanning/anchor` SDK hands a producer a set of `InclusionReceipt`s and can serialize them into one portable, self-verifying `sanning.evidence/v1` bundle (`body_type: sanning.anchor.trace/v1`). Verify the entire bundle (every event's signature + payload binding + Merkle inclusion, all offline) with the bundled CLI:
56
+ The `@sanning/anchor` SDK hands a producer a set of `InclusionReceipt`s and can serialize them into one portable, self-verifying `sanning.evidence/v1` bundle (`body_type: sanning.anchor.trace/v3`). Verify the entire bundle (every event's signature + payload binding + Merkle inclusion, all offline) with the bundled CLI:
57
57
 
58
58
  ```bash
59
59
  npx @sanning/proof verify trace-bundle.json
@@ -61,7 +61,7 @@ npx @sanning/proof verify trace-bundle.json
61
61
  npx @sanning/proof verify trace-bundle.json https://arweave.net,https://permagate.io
62
62
  ```
63
63
 
64
- It prints a per-event + rollup verdict and exits on a pinned code: `0` verified · `1` a real failure (bad signature / tamper / broken inclusion) · `2` malformed bundle · `3` gateway-unavailable when an on-chain re-fetch was requested. The producer's asserted `verdict` is shown but **never trusted**, the displayed verdict is always recomputed from the body. The same CLI also verifies the agent's `sanning.agent.proof/v1` inclusion bundle (it sniffs `spec_version`), so one command covers anchor + agent.
64
+ It prints a per-event + rollup verdict and exits on a pinned code: `0` verified · `1` a real failure (bad signature / tamper / broken inclusion) · `2` malformed bundle · `3` gateway-unavailable when an on-chain re-fetch was requested. Only `arweave` witnesses are re-fetched: a checkpoint or stamp whose `anchor_ref.kind` is anything else is not checked, and with gateways passed the run answers `partial`, exit `3`. The producer's asserted `verdict` is shown but **never trusted**, the displayed verdict is always recomputed from the body. The same CLI also verifies the agent's `sanning.agent.proof/v1` inclusion bundle (it sniffs `spec_version`), so one command covers anchor + agent.
65
65
 
66
66
  > The `verify` CLI is live in `@sanning/proof` ≥ 0.2.2, `npx @sanning/proof verify <bundle> [gateways]`. `verifyEvidenceBundle` is the programmatic API.
67
67
 
@@ -80,7 +80,7 @@ result.checkpoints; // per-checkpoint envelope / merkle-root / on-chain results
80
80
  result.events; // per-event signature / payload-binding / inclusion results
81
81
  ```
82
82
 
83
- A withheld `record_bytes` (external commitment, record not disclosed) leaves that event's payload binding **undetermined**, surfaced, never failed (the bundle stays cryptographically sound).
83
+ A withheld `event_bytes` (external commitment, record not disclosed) leaves that event's payload binding **undetermined**, surfaced, never failed (the bundle stays cryptographically sound).
84
84
 
85
85
  ### …including the raw logs, when the holder discloses them
86
86
 
@@ -184,7 +184,7 @@ const ok = await verifyInclusion(
184
184
  | Export | What it does |
185
185
  |---|---|
186
186
  | `verifyEnvelope(env, optionsOrContentHash?)` | The three load-bearing checks (spec-version registry, payload-hash recompute, Ed25519 over the signed scope) + optional content bind. Pass `{ payloadBytes }` for external-commitment envelopes. Returns `VerificationResult`. |
187
- | `verifyEvidenceBundle(bundle, { gateways?, fetchImpl? })` | Verify a signed `sanning.evidence/v1` / `sanning.anchor.trace/v1` bundle: wrapper signature + `body_hash` + every checkpoint + every event's inclusion, offline (optional on-chain re-fetch). Returns `EvidenceBundleResult`. Powers the `npx @sanning/proof verify` CLI. |
187
+ | `verifyEvidenceBundle(bundle, { gateways?, fetchImpl? })` | Verify a signed `sanning.evidence/v1` bundle (`sanning.anchor.trace/v3`, `sanning.stamp.trace/v1`, `sanning.stamp.evidence/v2` or `sanning.evidence.export/v2`): wrapper signature + `body_hash` + every checkpoint + every event's inclusion, offline (optional on-chain re-fetch). Returns `EvidenceBundleResult`. Powers the `npx @sanning/proof verify` CLI. |
188
188
  | `disclosedContent(bundle, { gateways?, content?, fetchImpl? })` | **Ingest:** verify a bundle, then hand back the DECODED raw bytes (`Uint8Array`) of every event whose disclosure bound to its committed `content_hash`, so materializing a received pack never means re-implementing the hex convention. Returns `DisclosedContentResult` (`content` map + per-event `verified` / `not_disclosed` / `undetermined` / `failed`, plus the full `verification`). |
189
189
  | `gradePack(result, account, notes)` + `accountDisclosure(bundle, content)` / `completenessNotes(bundle)` / `renderCause(cause)` | **Pack grading:** what a SURFACE may claim about a pack: the bundle plus whatever disclosed bytes travelled with it. Three states, never two: `verified` only when every record's own bytes bound to its own commitment and nothing is missing, unreadable, surplus or omitted; `failed` on a proven mismatch; `incomplete` when a check could not be run. Absent evidence is UNKNOWN, never an accusation. Pure: no I/O; transport stays yours. Conformance table: `vectors/pack-grading.json`, shipped in this tarball. |
190
190
  | `verifyAgentProofBundle(bundle, { gateways?, fetchImpl? })` / `isAgentProofSpec(v)` | Verify the agent producer's `sanning.agent.proof/v1` inclusion bundle. |
@@ -203,7 +203,7 @@ The primary signature covers `JCS(envelope minus signature minus co_signatures)`
203
203
 
204
204
  ## Why you can trust it (without trusting us)
205
205
 
206
- Three **independent implementations** of this kernel exist: this package, the Python [`sanning-proof`](https://pypi.org/project/sanning-proof/) (PyPI), and and the MIT-licensed Go reference. The TypeScript and Python kernels are conformance-gated **byte-for-byte** in CI against one shared corpus, pinned per file by SHA-256 at cut tag `test-vectors-v3.4`, and a cross-kernel agreement gate re-runs the corpus plus adversarial negatives through both, asserting identical verdicts on identical bytes. The Go reference runs as a third leg of that same gate, built at the commit its vendoring pins.
206
+ Three **independent implementations** of this kernel exist: this package, the Python [`sanning-proof`](https://pypi.org/project/sanning-proof/) (PyPI), and and the MIT-licensed Go reference. The TypeScript and Python kernels are conformance-gated **byte-for-byte** in CI against one shared corpus, pinned per file by SHA-256 at cut tag `test-vectors-v4.0`, and a cross-kernel agreement gate re-runs the corpus plus adversarial negatives through both, asserting identical verdicts on identical bytes. The Go reference runs as a third leg of that same gate, built at the commit its vendoring pins.
207
207
 
208
208
  That mutual gate is the point: anyone can verify these provenance records with **no Sanning code in the trust path**, write your own verifier against the specs that ship inside the [`sanning-proof`](https://pypi.org/project/sanning-proof/) sdist, and the corpus will tell you if it is conformant.
209
209
 
@@ -77,6 +77,11 @@ export async function verifyAgentProofBundle(bundle, options = {}) {
77
77
  errors.push("checkpoint envelope signature failed");
78
78
  if (res.payloadHashOk !== true)
79
79
  errors.push("checkpoint envelope payload binding failed");
80
+ // A malformed skeleton field (proof#95) fails the envelope with a valid
81
+ // signature and binding; name it rather than fail with no reason.
82
+ for (const m of res.errors)
83
+ if (m.startsWith("malformed "))
84
+ errors.push(`checkpoint envelope ${m}`);
80
85
  const payload = (b.checkpoint_envelope.payload ?? {});
81
86
  if (typeof payload.merkle_root === "string")
82
87
  merkleRoot = payload.merkle_root;
package/dist/chain.js CHANGED
@@ -2,8 +2,8 @@
2
2
  // event before it, and reporting what does not line up.
3
3
  //
4
4
  // WHY THIS LIVES IN THE KERNEL, BESIDE PACK GRADING.
5
- // `sanning.events/v1` records carry `previous_hash`, and until now nothing we
6
- // shipped read it. The envelope verifier hashes each event's `record_bytes`
5
+ // `sanning.events/v2` records carry `previous_hash`, and until now nothing we
6
+ // shipped read it. The envelope verifier hashes each event's `event_bytes`
7
7
  // and stops — `evidence.ts` says following the link is "consumer-layer logic
8
8
  // above it", and it was right that a SINGLE-envelope verifier should not do
9
9
  // it. But the consumer layer never did it either, so a bundle with an event
@@ -18,7 +18,7 @@
18
18
  // ---- WHAT THIS IS NOT ------------------------------------------------------
19
19
  //
20
20
  // It is not a completeness statement and it must never be read as one. The
21
- // profile says so in its own words (`anchor/docs/profile-sanning.events-v1.md`
21
+ // profile says so in its own words (`anchor/docs/profile-sanning.events-v2.md`
22
22
  // §5): chain semantics are "deletion/reorder-evidence for the events present
23
23
  // ... not a completeness proof." Drop the last N events of a chain and nothing
24
24
  // here dangles; the record that would have pointed at them was never written.
@@ -178,7 +178,7 @@ async function groupChains(bundle) {
178
178
  // event id an adapter pointer has nothing to resolve against.
179
179
  if (eventId === null || publicKey === null)
180
180
  continue;
181
- const decoded = decodeRecord(entry?.record_bytes);
181
+ const decoded = decodeRecord(entry?.event_bytes);
182
182
  if (decoded === null)
183
183
  continue;
184
184
  const { record, bytes } = decoded;
package/dist/cli.js CHANGED
@@ -2,8 +2,9 @@
2
2
  // npx @sanning/proof verify <bundle.json> [comma,sep,gateways]
3
3
  //
4
4
  // One turnkey verifier for both producer bundles in the stack:
5
- // - sanning.evidence/v1 (body sanning.anchor.trace/v1) — the @sanning/anchor SDK trace
6
- // - sanning.agent.proof/v1 — the agent inclusion proof
5
+ // - sanning.evidence/v1: bodies sanning.anchor.trace/v3, sanning.stamp.trace/v1,
6
+ // sanning.stamp.evidence/v2 and sanning.evidence.export/v2
7
+ // - sanning.agent.proof/v1: the agent inclusion proof
7
8
  // Dispatch is by sniffed spec_version; no flag tells the CLI which it is.
8
9
  //
9
10
  // Pinned exit codes (mirror `sanningd verify-status`):
@@ -163,8 +164,8 @@ export async function runCli(argv, io) {
163
164
  // `proof bundle <source-bundle.json> --attestations <f> --key <f> [-o <out>]`
164
165
  // (`proof export` still reaches this, undocumented — see the dispatch above.)
165
166
  //
166
- // Compose a signed, offline-verifiable sanning.evidence.export/v1 artifact from a
167
- // source sanning.anchor.trace/v1 bundle + operator attestation records + an exporter
167
+ // Compose a signed, offline-verifiable sanning.evidence.export/v2 artifact from a
168
+ // source sanning.anchor.trace/v3 bundle + operator attestation records + an exporter
168
169
  // Ed25519 key (evidence-export.md §5, composer). Writes the export to `-o <out>`
169
170
  // (or stdout when omitted, so it can be piped straight into `proof verify`).
170
171
  //
@@ -295,8 +296,8 @@ async function runBundle(rest, io) {
295
296
  function usageExport(io) {
296
297
  io.out("Usage: npx @sanning/proof bundle <source-bundle.json> --attestations <file> --key <file> [-o <out.json>]");
297
298
  io.out("");
298
- io.out("Compose a signed, offline-verifiable sanning.evidence.export/v1 artifact from an");
299
- io.out("sanning.anchor.trace/v1 source bundle + operator attestation records + an exporter");
299
+ io.out("Compose a signed, offline-verifiable sanning.evidence.export/v2 artifact from an");
300
+ io.out("sanning.anchor.trace/v3 source bundle + operator attestation records + an exporter");
300
301
  io.out("Ed25519 key. The output verifies with `proof verify <out.json>`.");
301
302
  io.out("");
302
303
  io.out("--attestations <file> JSON array of attestation records (or {attestations:[...]}).");
@@ -567,10 +568,12 @@ function printUsage(io) {
567
568
  io.out(" verify <bundle.json> [gateways] [--logs <path>] Verify a bundle");
568
569
  io.out(" bundle <source.json> --attestations <f> --key <f> [-o] Create an attested bundle");
569
570
  io.out("");
570
- io.out("Verify a sanning.evidence/v1 (sanning.anchor.trace/v1) bundle, an");
571
- io.out("sanning.evidence.export/v1 attested export, or a sanning.agent.proof/v1");
572
- io.out("inclusion bundle, fully offline. Supply a comma-separated gateway list");
573
- io.out("to also re-fetch each checkpoint on-chain.");
571
+ io.out("Verify a sanning.evidence/v1 bundle (body sanning.anchor.trace/v3,");
572
+ io.out("sanning.stamp.trace/v1, sanning.stamp.evidence/v2 or the attested export");
573
+ io.out("sanning.evidence.export/v2) or a sanning.agent.proof/v1 inclusion bundle,");
574
+ io.out("fully offline. Supply a comma-separated gateway list to also re-fetch");
575
+ io.out("each checkpoint and stamp on-chain. Only arweave witnesses are re-fetched;");
576
+ io.out("a witness of any other kind is not checked and the run exits 3.");
574
577
  io.out("");
575
578
  io.out("--logs <path> Bind disclosed raw logs to each event's committed");
576
579
  io.out(" content_hash. Either shape works, and which one you");
package/dist/compose.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // Attested-evidence-export composer (`proof export`, evidence-export.md §5,
2
2
  // phase 2). The last-mile issuer/runtime job the kernel does NOT own (§5.1
3
- // "Out of the kernel"): take a source `sanning.anchor.trace/v1` bundle + a set of
3
+ // "Out of the kernel"): take a source `sanning.anchor.trace/v3` bundle + a set of
4
4
  // operator attestation records + an exporter Ed25519 key, and PRODUCE one signed,
5
- // offline-verifiable `sanning.evidence.export/v1` artifact — exactly the bytes the
5
+ // offline-verifiable `sanning.evidence.export/v2` artifact, exactly the bytes the
6
6
  // kernel's verifyExportBody round-trip-verifies.
7
7
  //
8
8
  // This is the inverse of evidence.ts's verifyExportBody, and it is deliberately
@@ -24,7 +24,7 @@ import { EXPORT_BODY_TYPE, recomputeExportVerdict, } from "./evidence.js";
24
24
  import { base64UrlToBytes, bytesToHex, ed25519PublicKey, ed25519Sign, sha256Hex, utf8, } from "./crypto.js";
25
25
  import { jcs } from "./verifier.js";
26
26
  const GENESIS = "GENESIS";
27
- const EXPORT_SCHEMA = "sanning.evidence.export/v1";
27
+ const EXPORT_SCHEMA = EXPORT_BODY_TYPE;
28
28
  // Transcode an attestation `signature` to the lowercase hex §2.2 mandates. The
29
29
  // issuer emits either lowercase-hex or unpadded base64url; §2.2 stores hex, so
30
30
  // this is the composer's boundary normalization. Heuristic mirrors the repo's
package/dist/crypto.js CHANGED
@@ -72,7 +72,7 @@ export async function ed25519PublicKey(privateKeyHex) {
72
72
  return bytesToHex(await ed.getPublicKeyAsync(hexToBytes(privateKeyHex)));
73
73
  }
74
74
  // ---------------------------------------------------------------------------
75
- // RSA-PSS operator-attestation crypto (sanning.evidence.export/v1)
75
+ // RSA-PSS operator-attestation crypto (sanning.evidence.export/v2)
76
76
  //
77
77
  // The attested-evidence-export body embeds operator attestations signed with
78
78
  // RSA-PSS-SHA-256 over the JCS-canonical attestation payload; the operator key
@@ -1,23 +1,20 @@
1
1
  import type { Envelope } from "./types.js";
2
- export declare const ANCHOR_TRACE_BODY_TYPE = "sanning.anchor.trace/v2";
2
+ export declare const ANCHOR_TRACE_BODY_TYPE = "sanning.anchor.trace/v3";
3
3
  /**
4
- * The frozen pre-`anchor_ref` body (evidence-bundle.md §5.1.3).
5
- *
6
- * Accepted PERMANENTLY, not for a deprecation window. Every bundle emitted
7
- * before 2026-09-03 carries this shape, and those bundles are evidence — a
8
- * reader that stopped accepting them would destroy the record it exists to
9
- * defend (DL-30). Nothing new emits it.
4
+ * The name `event_bytes` replaced. Under the new majors a body entry that still
5
+ * carries it is a hard reject, never an ignored unknown field: ignored, it
6
+ * would read as a withheld record and the event would verify with nothing
7
+ * checked, the fail-open the new majors exist to prevent (governance.md §8,
8
+ * W-3).
10
9
  */
11
- export declare const ANCHOR_TRACE_BODY_TYPE_V1 = "sanning.anchor.trace/v1";
12
- /** Every anchor-trace body this kernel verifies. Order is newest-first. */
13
- export declare const ANCHOR_TRACE_BODY_TYPES: readonly ["sanning.anchor.trace/v2", "sanning.anchor.trace/v1"];
14
- export declare const EXPORT_BODY_TYPE = "sanning.evidence.export/v1";
10
+ export declare const LEGACY_EVENT_BYTES_FIELD = "record_bytes";
11
+ export declare const EXPORT_BODY_TYPE = "sanning.evidence.export/v2";
15
12
  /**
16
13
  * The Sanning-signed interval stamp body (evidence-bundle.md §5.2, DL-58).
17
14
  *
18
15
  * Its own `body_type` rather than a widened anchor-trace body, and the reason
19
16
  * is the whole decision: an anchor-trace checkpoint carries a PRODUCER-signed
20
- * `sanning.events/v1` envelope, and a stamp has no producer signature at all —
17
+ * `sanning.events/v2` envelope, and a stamp has no producer signature at all:
21
18
  * Sanning places it, under DL-52, for a customer who holds no chain identity.
22
19
  * The two rejected options were making `envelope` optional and synthesising an
23
20
  * envelope that Sanning signs; the second would have made a Sanning
@@ -25,7 +22,7 @@ export declare const EXPORT_BODY_TYPE = "sanning.evidence.export/v1";
25
22
  * unrecoverable once witnessed.
26
23
  */
27
24
  export declare const STAMP_TRACE_BODY_TYPE = "sanning.stamp.trace/v1";
28
- export declare const STAMP_EVIDENCE_BODY_TYPE = "sanning.stamp.evidence/v1";
25
+ export declare const STAMP_EVIDENCE_BODY_TYPE = "sanning.stamp.evidence/v2";
29
26
  /**
30
27
  * The version string INSIDE a witnessed stamp body — a different string from
31
28
  * the `body_type` above, and the two are easy to confuse.
@@ -42,7 +39,6 @@ export interface EvidenceIssuer {
42
39
  kind: string;
43
40
  tenant_id?: string;
44
41
  agent_id?: string;
45
- producer_id?: string;
46
42
  [k: string]: unknown;
47
43
  }
48
44
  export interface EvidenceVerdict {
@@ -71,9 +67,8 @@ export interface AnchorRef {
71
67
  export interface TraceCheckpoint {
72
68
  /**
73
69
  * The checkpoint's identity and the join key, DERIVED: hex SHA-256 of the
74
- * decoded `record_bytes`. Recomputed by the verifier — in v1 this was an
75
- * asserted label matched against another asserted label, so a producer could
76
- * relabel a checkpoint and the bundle still verified.
70
+ * decoded `event_bytes`. Recomputed by the verifier, never taken from the
71
+ * label.
77
72
  */
78
73
  checkpoint_ref: string;
79
74
  /**
@@ -83,14 +78,7 @@ export interface TraceCheckpoint {
83
78
  */
84
79
  anchor_ref?: AnchorRef;
85
80
  envelope: Envelope;
86
- record_bytes: string;
87
- merkle_root: string;
88
- }
89
- /** The frozen `sanning.anchor.trace/v1` checkpoint (§5.1.3). Read, never written. */
90
- export interface TraceCheckpointV1 {
91
- tx_id: string;
92
- envelope: Envelope;
93
- record_bytes: string;
81
+ event_bytes: string;
94
82
  merkle_root: string;
95
83
  }
96
84
  export interface TraceInclusion {
@@ -98,12 +86,12 @@ export interface TraceInclusion {
98
86
  leaf_index: number;
99
87
  leaf_count: number;
100
88
  audit_path: string[];
101
- /** Resolves against `checkpoints[].checkpoint_ref`. v1 spells this `checkpoint_tx_id`. */
89
+ /** Resolves against `checkpoints[].checkpoint_ref`. */
102
90
  checkpoint_ref: string;
103
91
  }
104
92
  export interface TraceEvent {
105
93
  envelope: Envelope;
106
- record_bytes?: string;
94
+ event_bytes?: string;
107
95
  content?: string;
108
96
  inclusion: TraceInclusion;
109
97
  }
@@ -174,7 +162,7 @@ export interface StampEvidenceInclusion {
174
162
  }
175
163
  export interface StampEvidenceEvent {
176
164
  envelope: Envelope;
177
- record_bytes?: string;
165
+ event_bytes?: string;
178
166
  content?: string;
179
167
  inclusion: StampEvidenceInclusion;
180
168
  }
@@ -211,23 +199,12 @@ export interface OnChainResult {
211
199
  perGateway: PerGatewayOutcome[];
212
200
  }
213
201
  export interface CheckpointResult {
214
- /**
215
- * The checkpoint's join key AS THE BUNDLE SPELLS IT — the derived
216
- * `checkpoint_ref` for a v2 body, the legacy `tx_id` for a frozen v1 body.
217
- * Never re-derived across forms: a v1 bundle's key is its `tx_id`, because
218
- * that is what its own events resolve against and what an attestation
219
- * written against it carries.
220
- */
202
+ /** The derived `checkpoint_ref`, empty when it could not be derived. */
221
203
  checkpointRef: string;
222
204
  /**
223
- * Where this checkpoint was witnessed, normalised to one shape.
224
- *
225
- * `null` when a v2 checkpoint carries no `anchor_ref` — placed but not yet
226
- * witnessed, which is a normal state and NOT a deficiency. For a frozen v1
227
- * checkpoint this is synthesised as `{ kind: "arweave", id: tx_id }`, which
228
- * is exactly what v1's spec said the field was ("the Arweave TX the
229
- * checkpoint envelope was anchored as"). The synthesis is reader-side only
230
- * and never re-enters a signed artifact or a verdict.
205
+ * Where this checkpoint was witnessed. `null` when the checkpoint carries no
206
+ * `anchor_ref`: placed but not yet witnessed, which is a normal state and NOT
207
+ * a deficiency.
231
208
  */
232
209
  anchorRef: AnchorRef | null;
233
210
  envelopeOk: boolean;
@@ -299,12 +276,14 @@ export interface StampResult {
299
276
  * ── The four states, and why none of them collapses ──
300
277
  *
301
278
  * `null` means not checked — no gateways were supplied, or this stamp names
302
- * no witness to check. It is a statement about the RUN, never about the
303
- * stamp. The other three are statements about the stamp and live in
304
- * `onChain.rollup`: `confirm` (a gateway returned the witnessed bytes),
305
- * `mismatch` (a gateway returned DIFFERENT bytes — a finding, and the only
306
- * one of the four that fails anything), `unreachable` (we asked and nobody
307
- * answered — an availability gap, never an accusation).
279
+ * no witness to check, or its `anchor_ref.kind` has no witness adapter, or
280
+ * its id is not one the adapter accepts; the witness was not checked. It is
281
+ * a statement about the RUN, never about the stamp. The other three are
282
+ * statements about the stamp and live in `onChain.rollup`: `confirm` (a
283
+ * gateway returned the witnessed bytes), `mismatch` (a gateway returned
284
+ * DIFFERENT bytes: a finding, and the only one of the four that fails
285
+ * anything), `unreachable` (we asked and nobody answered: an availability
286
+ * gap, never an accusation).
308
287
  *
309
288
  * Keeping "not checked" distinct from "unreachable" is the whole point. A
310
289
  * surface handed a collapsed boolean cannot tell "no gateway could be
@@ -339,11 +318,7 @@ export interface EvidenceBundleResult {
339
318
  errors: string[];
340
319
  }
341
320
  export interface AttestationRecord {
342
- /**
343
- * The source checkpoint this attestation binds to, spelled in the SOURCE
344
- * bundle's vocabulary: `checkpoint_ref` for a v2 source, the legacy `tx_id`
345
- * for a frozen v1 one. Not re-derived across forms.
346
- */
321
+ /** The source checkpoint's derived `checkpoint_ref` this attestation binds to. */
347
322
  checkpoint_ref: string;
348
323
  payload: AttestationPayload;
349
324
  signature_alg: string;