@sanning/proof 0.19.0 → 0.20.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.
Files changed (98) hide show
  1. package/README.md +30 -29
  2. package/dist/agent-record.d.ts +62 -0
  3. package/dist/agent-record.js +527 -0
  4. package/dist/agent-schemas.generated.d.ts +15 -0
  5. package/dist/agent-schemas.generated.js +5759 -0
  6. package/dist/case-file.d.ts +32 -89
  7. package/dist/case-file.js +41 -93
  8. package/dist/case.d.ts +343 -810
  9. package/dist/case.js +693 -1151
  10. package/dist/chain.d.ts +19 -47
  11. package/dist/chain.js +78 -205
  12. package/dist/claims.d.ts +211 -0
  13. package/dist/claims.js +1081 -0
  14. package/dist/cli.d.ts +45 -68
  15. package/dist/cli.js +618 -1022
  16. package/dist/compose.js +23 -65
  17. package/dist/crypto.d.ts +7 -8
  18. package/dist/crypto.js +60 -147
  19. package/dist/declaration.d.ts +68 -194
  20. package/dist/declaration.js +175 -518
  21. package/dist/environment.d.ts +9 -30
  22. package/dist/environment.js +17 -66
  23. package/dist/evidence.d.ts +119 -160
  24. package/dist/evidence.js +744 -1103
  25. package/dist/exit-codes.d.ts +11 -27
  26. package/dist/exit-codes.js +24 -59
  27. package/dist/file-match.d.ts +33 -79
  28. package/dist/file-match.js +33 -69
  29. package/dist/grading.d.ts +98 -135
  30. package/dist/grading.js +224 -380
  31. package/dist/handoff.d.ts +62 -160
  32. package/dist/handoff.js +153 -393
  33. package/dist/index.d.ts +14 -10
  34. package/dist/index.js +60 -167
  35. package/dist/json-schema.d.ts +29 -0
  36. package/dist/json-schema.js +298 -0
  37. package/dist/merkle.js +16 -28
  38. package/dist/message-parts.d.ts +56 -212
  39. package/dist/message-parts.js +142 -502
  40. package/dist/side-content.d.ts +11 -22
  41. package/dist/side-content.js +22 -65
  42. package/dist/types.d.ts +17 -0
  43. package/dist/types.js +19 -5
  44. package/dist/verifier.d.ts +40 -114
  45. package/dist/verifier.js +196 -535
  46. package/dist/witness-cache.d.ts +24 -45
  47. package/dist/witness-cache.js +38 -81
  48. package/dist/witness-lookup.d.ts +36 -57
  49. package/dist/witness-lookup.js +89 -139
  50. package/dist/zip-reader.d.ts +18 -33
  51. package/dist/zip-reader.js +19 -34
  52. package/package.json +11 -3
  53. package/src/agent-record.ts +608 -0
  54. package/src/agent-schemas.generated.ts +5764 -0
  55. package/src/case-file.ts +45 -109
  56. package/src/case.ts +848 -1322
  57. package/src/chain.ts +85 -224
  58. package/src/claims.ts +1253 -0
  59. package/src/cli.ts +714 -1176
  60. package/src/compose.ts +33 -79
  61. package/src/crypto.ts +60 -147
  62. package/src/declaration.ts +219 -656
  63. package/src/environment.ts +17 -65
  64. package/src/evidence.ts +958 -1381
  65. package/src/exit-codes.ts +24 -63
  66. package/src/file-match.ts +40 -89
  67. package/src/grading.ts +287 -421
  68. package/src/handoff.ts +175 -427
  69. package/src/index.ts +132 -177
  70. package/src/json-schema.ts +295 -0
  71. package/src/merkle.ts +16 -28
  72. package/src/message-parts.ts +155 -533
  73. package/src/side-content.ts +25 -68
  74. package/src/types.ts +55 -36
  75. package/src/verifier.ts +202 -526
  76. package/src/witness-cache.ts +45 -94
  77. package/src/witness-lookup.ts +107 -177
  78. package/src/zip-reader.ts +20 -38
  79. package/vectors/README.md +52 -34
  80. package/vectors/agent-record.json +2175 -0
  81. package/vectors/case-file-index.json +1 -1
  82. package/vectors/case-split.json +2 -2
  83. package/vectors/chain-reading.json +3 -3
  84. package/vectors/file-match.json +285 -148
  85. package/vectors/jcs-reader.json +6 -6
  86. package/vectors/pack-grading.json +102 -25
  87. package/vectors/sandbox-evidence.json +597 -580
  88. package/vectors/stamp-on-chain.json +18 -18
  89. package/vectors/witness-cache.json +49 -49
  90. package/dist/agent-proof.d.ts +0 -28
  91. package/dist/agent-proof.js +0 -226
  92. package/dist/run-adapters.d.ts +0 -10
  93. package/dist/run-adapters.js +0 -35
  94. package/dist/run.d.ts +0 -209
  95. package/dist/run.js +0 -1405
  96. package/src/agent-proof.ts +0 -264
  97. package/src/run-adapters.ts +0 -44
  98. package/src/run.ts +0 -1598
@@ -1,85 +1,52 @@
1
- // The witnessed-time cache: what a pack remembers about its witness, unverified
2
- // (evidence-bundle.md 5.6.6, v1.25, A30, proof#24).
1
+ // The witnessed-time cache (evidence-bundle.md §5.6.6): `witness-cache.json`
2
+ // beside a pack's `bundle.json`, giving the block height and time of each
3
+ // stamp's witness transaction as the pack builder fetched them. Nobody signs it
4
+ // and anyone can edit it, so every member is unverified.
3
5
  //
4
- // The TypeScript half of `src/sanning_proof/witness_cache.py`. Both kernels are
5
- // held to one table, `ts/vectors/witness-cache.json`, so neither can pass by
6
- // agreeing with the other.
6
+ // Trust rules:
7
+ // - `readWitnessCache` is never an input to the bundle verdict, the pack grade
8
+ // or the case reading.
9
+ // - `checkWitnessCache` asks the public record. A different block is a finding
10
+ // (`differs`); an unsettled lookup leaves the value `not_checked`.
11
+ // - Nothing here returns a time a reader may rely on. A gateway is named, never
12
+ // trusted.
7
13
  //
8
- // WHAT THE CACHE IS. A file beside the pack's `bundle.json`,
9
- // `witness-cache.json`, and never a member of the bundle. Its one list today,
10
- // `witnessed`, gives for each stamp the block height and block time of the
11
- // witness transaction, as the builder of the pack fetched them. Nobody signs
12
- // it, and anyone holding the pack can edit it. EVERY MEMBER OF THE FILE IS
13
- // UNVERIFIED BY CONSTRUCTION, and the name carries that warning to every field
14
- // added to it later.
15
- //
16
- // Because it is a separate file, a reader before v1.25 never opens it: it lists
17
- // it as a file outside every pack and reads the pack exactly as it would
18
- // without it. A `cache` member written INSIDE `bundle.json` is an unknown
19
- // wrapper field, counted in the signed scope by every reader, this one
20
- // included.
21
- //
22
- // WHAT THIS MODULE DOES, AND WHAT IT MUST NEVER DO.
23
- //
24
- // - `readWitnessCache` reads the file's bytes against the signed bundle beside
25
- // it. It is pure, and it is never an input to the bundle verdict, the pack
26
- // grade or the case reading: neither `verifyEvidenceBundle` nor `readCase` is
27
- // ever handed the file.
28
- // - `checkWitnessCache` looks each cached time up on the public record, where a
29
- // caller asked for a lookup. A lookup that answers with a different block is
30
- // a finding, reported on the witness line as `differs`. A lookup that does
31
- // not settle leaves the cached value labelled not checked.
32
- // - Nothing here returns a time a reader may rely on. The only time this module
33
- // ever reports as the public record's is one a gateway returned, and a
34
- // gateway is named, never trusted (6).
35
- //
36
- // A file that is absent, that is not JSON under the reader's rule, whose
37
- // `cache_version` is not `WITNESS_CACHE_VERSION`, whose `witnessed` is not a
38
- // list, or whose entry is not well formed, reads as no cache for what it could
39
- // not say. It is never a finding and never moves anything.
14
+ // A file that is absent, malformed, of another `cache_version`, or with a bad
15
+ // entry reads as no cache for what it could not say. It is never a finding.
40
16
 
41
17
  import { jsonDepthExceeds } from "./case-file.js";
42
18
  import { jcs, jcsParse } from "./verifier.js";
43
19
 
44
20
  /**
45
- * The last second a block time can name and still be printed the same way by
46
- * both kernels: 9999-12-31T23:59:59Z. A larger value is a cache entry this
47
- * reader cannot print, and reads as none.
21
+ * The largest printable block time, 9999-12-31T23:59:59Z. A larger value reads
22
+ * as no entry.
48
23
  */
49
24
  export const MAX_BLOCK_TIME = 253402300799;
50
25
 
51
- /** The file's name, beside `bundle.json` in the pack's folder (5.6.1). */
26
+ /** The file's name, beside `bundle.json` (§5.6.1). */
52
27
  export const WITNESS_CACHE_NAME = "witness-cache.json";
53
28
 
54
- /**
55
- * The dispatch key: a file whose `cache_version` is absent or any other value
56
- * reads as no cache.
57
- */
29
+ /** Any other or absent `cache_version` reads as no cache. */
58
30
  export const WITNESS_CACHE_VERSION = "sanning.witness-cache/v1";
59
31
 
60
- /**
61
- * How deep `[` and `{` may nest in the file before it is refused unread: the
62
- * limit `bundle.json` and `contents.json` are read under.
63
- */
32
+ /** Nesting limit for the file, the same as for `bundle.json` and `contents.json`. */
64
33
  export const MAX_WITNESS_CACHE_DEPTH = 512;
65
34
 
66
35
  // The bodies that carry `stamps[]`, the only witnesses the cache speaks for.
67
36
  const STAMP_BODIES = new Set(["sanning.stamp.trace/v1", "sanning.stamp.evidence/v2"]);
68
37
  const STAMP_REF = /^[0-9a-f]{64}$/;
69
- // The one witness kind a time is looked up for, and the only ids the lookup
70
- // sends: the same rule the byte lookup applies (evidence-bundle 5.1.1, v1.6).
38
+ // The only witness kind and id shape a lookup sends, as for the byte lookup
39
+ // (evidence-bundle §5.1.1).
71
40
  const ADAPTER_KIND = "arweave";
72
41
  const ARWEAVE_ID = /^[A-Za-z0-9_-]{43}$/;
73
- // A GraphQL answer to one query for one transaction is a few hundred bytes. A
74
- // deeper answer is not one, and refusing it before any parser recurses keeps
75
- // the two kernels on one reading of it.
42
+ // An answer for one transaction is a few hundred bytes. Refusing a deeper one
43
+ // before parsing keeps every reader on the same result.
76
44
  const MAX_ANSWER_DEPTH = 16;
77
45
 
78
46
  /**
79
- * One `cache.witnessed[]` entry that reads, joined to the stamp it names.
80
- * `witnessKind` and `witnessId` are the stamp's own `anchor_ref`, read from the
81
- * signed body, never from the cache: the cache says WHEN, and the body alone
82
- * says WHICH witness it is about.
47
+ * One readable `cache.witnessed[]` entry joined to its stamp. `witnessKind` and
48
+ * `witnessId` come from the signed body, never the cache: the cache says when,
49
+ * the body alone says which witness.
83
50
  */
84
51
  export interface CachedWitnessTime {
85
52
  stampRef: string;
@@ -90,11 +57,8 @@ export interface CachedWitnessTime {
90
57
  }
91
58
 
92
59
  /**
93
- * One gateway's answer for one cached time. `matches`: the gateway named the
94
- * cached block height and block time. `differs`: it named a block, and the
95
- * block is not the cached one. `unreachable`: it named no block (a transport
96
- * failure, a refusal, an answer that is not one, or a witness not yet in a
97
- * block).
60
+ * One gateway's answer for one cached time. `unreachable` means it named no
61
+ * block: transport failure, refusal, malformed answer, or not yet mined.
98
62
  */
99
63
  export interface WitnessTimeAnswer {
100
64
  gateway: string;
@@ -104,10 +68,8 @@ export interface WitnessTimeAnswer {
104
68
  }
105
69
 
106
70
  /**
107
- * One cached time, and what the public record said about it: `differs` when
108
- * any gateway named a different block, otherwise `matches` when any gateway
109
- * named the cached one, otherwise `not_checked`. The same worst-answer-wins
110
- * order as the byte lookup.
71
+ * One cached time and the public record's answer: `differs` if any gateway
72
+ * named another block, else `matches` if any named this one, else `not_checked`.
111
73
  */
112
74
  export interface WitnessTimeCheck {
113
75
  cached: CachedWitnessTime;
@@ -119,7 +81,6 @@ function isObject(v: unknown): v is Record<string, unknown> {
119
81
  return v !== null && typeof v === "object" && !Array.isArray(v);
120
82
  }
121
83
 
122
- // A member the object itself holds, never one inherited from its prototype.
123
84
  function own(o: Record<string, unknown>, key: string): unknown {
124
85
  return Object.prototype.hasOwnProperty.call(o, key) ? o[key] : undefined;
125
86
  }
@@ -130,8 +91,8 @@ function blockNumber(value: unknown, upper: number): number | null {
130
91
  return value;
131
92
  }
132
93
 
133
- // `stamp_ref` to `[kind, id]` for every stamp in the signed body that names a
134
- // witness. A `stamp_ref` spelled on two stamps names neither.
94
+ // `stamp_ref` to `[kind, id]` for each signed stamp that names a witness. A
95
+ // `stamp_ref` on two stamps names neither.
135
96
  function stampWitnesses(bundle: Record<string, unknown>): Map<string, [string, string]> {
136
97
  const out = new Map<string, [string, string]>();
137
98
  if (typeof bundle.body_type !== "string" || !STAMP_BODIES.has(bundle.body_type)) return out;
@@ -157,10 +118,8 @@ function stampWitnesses(bundle: Record<string, unknown>): Map<string, [string, s
157
118
  return out;
158
119
  }
159
120
 
160
- // The file's JSON, read under the rule `contents.json` is read under (5.6.2):
161
- // strict UTF-8, no byte-order mark, no `NaN` or `Infinity`, no member named
162
- // twice, and nothing nested past `MAX_WITNESS_CACHE_DEPTH`. `undefined` for
163
- // anything else.
121
+ // Strict JSON as for `contents.json` (§5.6.2), within `MAX_WITNESS_CACHE_DEPTH`.
122
+ // `undefined` for anything else.
164
123
  function readFile(raw: Uint8Array | null | undefined): unknown {
165
124
  if (!(raw instanceof Uint8Array)) return undefined;
166
125
  if (jsonDepthExceeds(raw, MAX_WITNESS_CACHE_DEPTH)) return undefined;
@@ -172,17 +131,13 @@ function readFile(raw: Uint8Array | null | undefined): unknown {
172
131
  }
173
132
 
174
133
  /**
175
- * The cached witness times the `witness-cache.json` bytes `raw` give for the
176
- * signed `bundle` beside them, in the order the file lists them. `raw` is
177
- * `null` where the pack has no such file. Pure, and never throws. The file
178
- * reads only where it is a JSON object whose `cache_version` is
179
- * `WITNESS_CACHE_VERSION` and whose `witnessed` is a list. An entry reads only
180
- * where it is an object whose `stamp_ref` is 64 lowercase hex characters naming
181
- * exactly one stamp of the signed body that has an `anchor_ref`, whose
182
- * `block_height` is a non-negative safe integer, and whose `block_time` is a
183
- * whole number of seconds since 1970-01-01T00:00:00Z from 0 to
184
- * `MAX_BLOCK_TIME`. A `stamp_ref` two entries name reads for neither.
185
- * Everything else reads as no cache for the stamp it would have spoken for.
134
+ * The cached witness times `raw` gives for the signed `bundle`, in file order.
135
+ * `raw` is `null` where the pack has no cache file. Pure; never throws.
136
+ *
137
+ * An entry reads only if its `stamp_ref` names exactly one signed stamp with an
138
+ * `anchor_ref`, `block_height` is a non-negative safe integer, and `block_time`
139
+ * is Unix seconds from 0 to `MAX_BLOCK_TIME`. A `stamp_ref` named twice reads
140
+ * for neither.
186
141
  */
187
142
  export function readWitnessCache(bundle: unknown, raw: Uint8Array | null | undefined): CachedWitnessTime[] {
188
143
  if (!isObject(bundle)) return [];
@@ -208,7 +163,7 @@ export function readWitnessCache(bundle: unknown, raw: Uint8Array | null | undef
208
163
  return read.filter((c) => named.get(c.stampRef) === 1);
209
164
  }
210
165
 
211
- /** A block time as RFC 3339 UTC to the second, the way both kernels print it: `2026-09-08T12:10:07Z`. */
166
+ /** A block time as RFC 3339 UTC to the second: `2026-09-08T12:10:07Z`. */
212
167
  export function formatBlockTime(seconds: number): string {
213
168
  return new Date(seconds * 1000).toISOString().replace(/\.\d{3}Z$/, "Z");
214
169
  }
@@ -218,10 +173,7 @@ export function renderWitnessTime(blockHeight: number, blockTime: number): strin
218
173
  return `block ${blockHeight} at ${formatBlockTime(blockTime)}`;
219
174
  }
220
175
 
221
- /**
222
- * The request body the lookup sends: one GraphQL query for one transaction's
223
- * block, as JCS, so both kernels send the same bytes.
224
- */
176
+ /** The lookup's request body: one GraphQL query as JCS, so the bytes are fixed. */
225
177
  export function witnessTimeQuery(witnessId: string): string {
226
178
  return jcs({ query: `query{transaction(id:"${witnessId}"){block{height timestamp}}}` });
227
179
  }
@@ -272,10 +224,9 @@ async function ask(
272
224
  }
273
225
 
274
226
  /**
275
- * Look each cached time up on every gateway, in order. With no gateways or no
276
- * `fetchImpl`, nothing is looked up and every time is `not_checked`. A witness
277
- * that is not `arweave`, or whose id is not a 43-character base64url
278
- * transaction id, is never looked up.
227
+ * Looks each cached time up on every gateway, in order. With no gateways or no
228
+ * `fetchImpl`, every time is `not_checked`. Only `arweave` witnesses with a
229
+ * 43-character base64url id are looked up.
279
230
  */
280
231
  export async function checkWitnessCache(
281
232
  cached: readonly CachedWitnessTime[],