@sanning/proof 0.19.1 → 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 +339 -822
  9. package/dist/case.js +667 -1160
  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 +814 -1328
  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 +131 -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 +573 -581
  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,42 +1,21 @@
1
1
  import { type EvidenceBundleResult } from "./evidence.js";
2
2
  /** `"dev"` when every envelope is signed `dev`, `"mixed"` when only some are. */
3
3
  export type EnvironmentMarker = "dev" | "mixed";
4
- /** The headline `verify` prints for sandbox evidence in place of `VERIFIED`. */
5
- export declare const SANDBOX_HEADLINE = "SANDBOX";
6
- /** The title `case` prints for sandbox evidence in place of `Verified` (case titles are title case). */
7
- export declare const SANDBOX_TITLE = "Sandbox";
8
- /** The one line printed with it, and with any other headline, wherever the marker is present. */
9
- export declare const SANDBOX_LINE = "Sandbox evidence (environment dev): not production evidence";
10
4
  /**
11
- * The signed envelopes a pack holds: every checkpoint and event envelope of an
12
- * anchor-trace body, every event envelope of a stamp-evidence body, the same
13
- * inside an export's inline source bundle, and an agent proof's checkpoint
14
- * envelope. A stamp-trace body holds none. A value that is not an object is
15
- * not an envelope and is not counted.
5
+ * The signed envelopes a pack holds: checkpoint and event envelopes of an
6
+ * anchor-trace body, event envelopes of a stamp-evidence body (also inside an
7
+ * export's inline source bundle). Non-objects are not counted.
16
8
  */
17
9
  export declare function packEnvelopes(bundle: unknown): Record<string, unknown>[];
18
10
  /**
19
- * The environment marker over one or more packs: `"dev"` where every envelope
20
- * they hold is signed `environment: "dev"`, `"mixed"` where some are and some
21
- * are not (`production`, or no `environment` at all), and `null` where none
22
- * is, which is every production pack and every pack that holds no envelope.
11
+ * The marker over one or more packs: `"dev"` where every envelope is signed
12
+ * `environment: "dev"`, `"mixed"` where only some are, `null` where none is
13
+ * (including a pack that holds no envelope).
23
14
  */
24
15
  export declare function environmentMarker(bundles: readonly unknown[]): EnvironmentMarker | null;
25
16
  /**
26
- * Whether a body's only unsettled witnesses are simulated: the body has events
27
- * and witnesses (checkpoints, stamps or both), every witness is named, at
28
- * least one is simulated, and every other one a gateway confirmed. Then the
29
- * one thing that keeps the bundle from `verified` is a simulated witness
30
- * nobody can look up: no mismatch, no unreachable gateway, no witness of
31
- * another kind, no checkpoint or stamp witnessed by nobody.
32
- */
33
- export declare function onlySimulatedUnsettled(result: EvidenceBundleResult): boolean;
34
- /**
35
- * Whether a run with gateways counted only simulated witnesses, so nothing
36
- * was looked up on a chain: gateways were passed, the run is not counted as
37
- * checked on a chain, and it holds a simulated witness. A run that stopped
38
- * before its witnesses were read (a bad wrapper signature or `body_hash`, an
39
- * unknown `body_type`, an export whose source is not inline) holds none, so it
40
- * is not one, and prints what it printed before simulated witnesses existed.
17
+ * Whether a run with gateways counted only simulated witnesses, so nothing was
18
+ * looked up on a chain. A run that stopped before reading its witnesses holds
19
+ * none and is not one.
41
20
  */
42
21
  export declare function simulatedOnlyRun(result: EvidenceBundleResult, gateways: readonly string[]): boolean;
@@ -1,27 +1,13 @@
1
- // The environment marker: is this sandbox evidence?
1
+ // The environment marker: whether a pack holds sandbox evidence.
2
2
  //
3
- // `environment` is an OPTIONAL skeleton field inside every envelope's signed
4
- // scope, `"dev"` or `"production"` (envelope-spec §2, A1 and A9). The spec
5
- // says a verifier MAY surface the value as `environment_marker` in its result
6
- // and a profile MAY refuse to count dev-marked envelopes toward production
7
- // evidence. Both commands now do the first, and print a pack or case that
8
- // holds a `dev` envelope as sandbox evidence rather than as `VERIFIED`.
9
- //
10
- // The marker is read from the signed envelopes the pack holds, never from a
11
- // wrapper field, a pack name or anything else the holder could write without a
12
- // key. It never moves the verdict word: a sandbox pack that verifies is still
13
- // `verified`, and the commands map that to the headline `SANDBOX` and exit 5
14
- // (`sandboxExit` in `./exit-codes.ts`). The Python twin is
15
- // `src/sanning_proof/environment.py`.
16
- import { isAgentProofSpec } from "./agent-proof.js";
3
+ // `environment` is an optional field inside every envelope's signed scope,
4
+ // `"dev"` or `"production"` (envelope-spec section 2). It is read from the signed
5
+ // envelopes a pack holds, never from a wrapper field, a pack name or anything
6
+ // else the holder could write without a key. It is a label, never a claim or a
7
+ // verdict: a sandbox pack that verifies is still `verified`, prints the sandbox
8
+ // line, and exits 3 (`exitCodeFor`).
17
9
  import { ANCHOR_TRACE_BODY_TYPE, EXPORT_BODY_TYPE, SIMULATED_WITNESS_KIND, STAMP_EVIDENCE_BODY_TYPE, } from "./evidence.js";
18
- /** The headline `verify` prints for sandbox evidence in place of `VERIFIED`. */
19
- export const SANDBOX_HEADLINE = "SANDBOX";
20
- /** The title `case` prints for sandbox evidence in place of `Verified` (case titles are title case). */
21
- export const SANDBOX_TITLE = "Sandbox";
22
- /** The one line printed with it, and with any other headline, wherever the marker is present. */
23
- export const SANDBOX_LINE = "Sandbox evidence (environment dev): not production evidence";
24
- // The same bound `resolveTraceEvents` puts on unwrapping export wrappers.
10
+ // Same bound as `resolveTraceEvents` puts on unwrapping export wrappers.
25
11
  const MAX_EXPORT_UNWRAP = 4;
26
12
  function isObject(v) {
27
13
  return v !== null && typeof v === "object" && !Array.isArray(v);
@@ -41,20 +27,12 @@ function entryEnvelopes(list, out) {
41
27
  }
42
28
  }
43
29
  /**
44
- * The signed envelopes a pack holds: every checkpoint and event envelope of an
45
- * anchor-trace body, every event envelope of a stamp-evidence body, the same
46
- * inside an export's inline source bundle, and an agent proof's checkpoint
47
- * envelope. A stamp-trace body holds none. A value that is not an object is
48
- * not an envelope and is not counted.
30
+ * The signed envelopes a pack holds: checkpoint and event envelopes of an
31
+ * anchor-trace body, event envelopes of a stamp-evidence body (also inside an
32
+ * export's inline source bundle). Non-objects are not counted.
49
33
  */
50
34
  export function packEnvelopes(bundle) {
51
35
  const out = [];
52
- if (isObject(bundle) && isAgentProofSpec(own(bundle, "spec_version"))) {
53
- const env = own(bundle, "checkpoint_envelope");
54
- if (isObject(env))
55
- out.push(env);
56
- return out;
57
- }
58
36
  let cur = bundle;
59
37
  for (let depth = 0; depth < MAX_EXPORT_UNWRAP; depth++) {
60
38
  if (!isObject(cur))
@@ -79,10 +57,9 @@ export function packEnvelopes(bundle) {
79
57
  return out;
80
58
  }
81
59
  /**
82
- * The environment marker over one or more packs: `"dev"` where every envelope
83
- * they hold is signed `environment: "dev"`, `"mixed"` where some are and some
84
- * are not (`production`, or no `environment` at all), and `null` where none
85
- * is, which is every production pack and every pack that holds no envelope.
60
+ * The marker over one or more packs: `"dev"` where every envelope is signed
61
+ * `environment: "dev"`, `"mixed"` where only some are, `null` where none is
62
+ * (including a pack that holds no envelope).
86
63
  */
87
64
  export function environmentMarker(bundles) {
88
65
  let total = 0;
@@ -99,35 +76,9 @@ export function environmentMarker(bundles) {
99
76
  return dev === total ? "dev" : "mixed";
100
77
  }
101
78
  /**
102
- * Whether a body's only unsettled witnesses are simulated: the body has events
103
- * and witnesses (checkpoints, stamps or both), every witness is named, at
104
- * least one is simulated, and every other one a gateway confirmed. Then the
105
- * one thing that keeps the bundle from `verified` is a simulated witness
106
- * nobody can look up: no mismatch, no unreachable gateway, no witness of
107
- * another kind, no checkpoint or stamp witnessed by nobody.
108
- */
109
- export function onlySimulatedUnsettled(result) {
110
- const witnesses = [...result.checkpoints, ...(result.stamps ?? [])];
111
- if (witnesses.length === 0 || result.events.length === 0)
112
- return false;
113
- let simulated = 0;
114
- for (const w of witnesses) {
115
- if (w.anchorRef === null)
116
- return false;
117
- if (w.anchorRef.kind === SIMULATED_WITNESS_KIND)
118
- simulated++;
119
- else if (w.onChain?.rollup !== "confirm")
120
- return false;
121
- }
122
- return simulated > 0;
123
- }
124
- /**
125
- * Whether a run with gateways counted only simulated witnesses, so nothing
126
- * was looked up on a chain: gateways were passed, the run is not counted as
127
- * checked on a chain, and it holds a simulated witness. A run that stopped
128
- * before its witnesses were read (a bad wrapper signature or `body_hash`, an
129
- * unknown `body_type`, an export whose source is not inline) holds none, so it
130
- * is not one, and prints what it printed before simulated witnesses existed.
79
+ * Whether a run with gateways counted only simulated witnesses, so nothing was
80
+ * looked up on a chain. A run that stopped before reading its witnesses holds
81
+ * none and is not one.
131
82
  */
132
83
  export function simulatedOnlyRun(result, gateways) {
133
84
  if (gateways.length === 0 || result.onChainChecked)
@@ -1,40 +1,26 @@
1
- import type { Envelope } from "./types.js";
2
- /** The accepted `sanning.evidence/v1` wrapper majors; exported in both kernels (P-01, 0.13.0). */
1
+ import { type AgentRecordReading } from "./agent-record.js";
2
+ import type { Envelope, EnvelopeFault } from "./types.js";
3
+ /** The accepted evidence-bundle wrapper majors. */
3
4
  export declare const ACCEPTED_EVIDENCE_MAJORS: readonly string[];
4
5
  export declare const ANCHOR_TRACE_BODY_TYPE = "sanning.anchor.trace/v3";
5
6
  /**
6
- * The name `event_bytes` replaced. Under the new majors a body entry that still
7
- * carries it is a hard reject, never an ignored unknown field: ignored, it
8
- * would read as a withheld record and the event would verify with nothing
9
- * checked, the fail-open the new majors exist to prevent (governance.md §8,
10
- * W-3).
7
+ * The field name `event_bytes` replaced. A body entry still carrying it is a
8
+ * hard reject: ignored, it would read as a withheld record and verify with
9
+ * nothing checked.
11
10
  */
12
11
  export declare const LEGACY_EVENT_BYTES_FIELD = "record_bytes";
13
12
  export declare const EXPORT_BODY_TYPE = "sanning.evidence.export/v2";
14
13
  /**
15
- * The Sanning-signed interval stamp body (evidence-bundle.md §5.2, DL-58).
16
- *
17
- * Its own `body_type` rather than a widened anchor-trace body, and the reason
18
- * is the whole decision: an anchor-trace checkpoint carries a PRODUCER-signed
19
- * `sanning.events/v2` envelope, and a stamp has no producer signature at all:
20
- * Sanning places it, under DL-52, for a customer who holds no chain identity.
21
- * The two rejected options were making `envelope` optional and synthesising an
22
- * envelope that Sanning signs; the second would have made a Sanning
23
- * attestation indistinguishable ON THE WIRE from a producer's, which is
24
- * unrecoverable once witnessed.
14
+ * The Sanning-signed interval stamp body (evidence-bundle.md §5.2). Its own
15
+ * body type, because a stamp has no producer signature: a Sanning attestation
16
+ * must never be indistinguishable on the wire from a producer's.
25
17
  */
26
18
  export declare const STAMP_TRACE_BODY_TYPE = "sanning.stamp.trace/v1";
27
19
  export declare const STAMP_EVIDENCE_BODY_TYPE = "sanning.stamp.evidence/v2";
28
20
  /**
29
- * The version string INSIDE a witnessed stamp body — a different string from
30
- * the `body_type` above, and the two are easy to confuse.
31
- *
32
- * `sanning.stamp.trace/v1` discriminates the BUNDLE BODY: it is the reader's
33
- * dispatch key. `sanning.stamp/v1` is the control plane's own version of the
34
- * bytes it hands a witness (`control-plane/src/stamp-tree.ts`,
35
- * `STAMP_BODY_VERSION`). A `sanning.stamp.trace/v1` body pins the inner
36
- * version to exactly this value: two answers to one question inside one
37
- * signature is the §5.1.2 mixed-vocabulary reject, one layer down.
21
+ * The version string inside a witnessed stamp body, distinct from the
22
+ * `body_type` above (the dispatch key). A stamp trace body pins it to exactly
23
+ * this value (§5.1.2).
38
24
  */
39
25
  export declare const STAMP_BODY_VERSION = "sanning.stamp/v1";
40
26
  export interface EvidenceIssuer {
@@ -43,23 +29,9 @@ export interface EvidenceIssuer {
43
29
  agent_id?: string;
44
30
  [k: string]: unknown;
45
31
  }
46
- export interface EvidenceVerdict {
47
- status: string;
48
- summary?: string;
49
- counts?: Record<string, number>;
50
- as_of?: string;
51
- [k: string]: unknown;
52
- }
53
32
  /**
54
- * Where a commitment was witnessed (evidence-bundle.md §5.1.1).
55
- *
56
- * The same three-part shape the control plane indexes (`kind` / one promoted
57
- * `id` / a typed remainder), so the signed body, the index row and the SDK
58
- * artifact stop being three vocabularies for one fact.
59
- *
60
- * NOT ONE FIELD HERE NAMES A CHAIN. Arweave's chunk proof, data_path, offsets
61
- * and packing live in `ref`, opaque to every reader above this seam. A field
62
- * that would need renaming for a second witness belongs in `ref` by definition.
33
+ * Where a commitment was witnessed (evidence-bundle.md §5.1.1). No field here
34
+ * names a chain: chain-specific detail lives in the opaque `ref`.
63
35
  */
64
36
  export interface AnchorRef {
65
37
  kind: string;
@@ -68,15 +40,13 @@ export interface AnchorRef {
68
40
  }
69
41
  export interface TraceCheckpoint {
70
42
  /**
71
- * The checkpoint's identity and the join key, DERIVED: hex SHA-256 of the
72
- * decoded `event_bytes`. Recomputed by the verifier, never taken from the
73
- * label.
43
+ * Hex SHA-256 of the decoded `event_bytes`: the checkpoint's identity and join
44
+ * key. Recomputed by the verifier, never trusted.
74
45
  */
75
46
  checkpoint_ref: string;
76
47
  /**
77
- * Where it was witnessed, IF IT HAS BEEN. Optional, and absent is honest:
78
- * under DL-52 a checkpoint is routinely placed before it is witnessed, and a
79
- * required field that must be filled dishonestly is a defect in the format.
48
+ * Where it was witnessed, if it has been. Absent is honest: checkpoints are
49
+ * placed before they are witnessed.
80
50
  */
81
51
  anchor_ref?: AnchorRef;
82
52
  envelope: Envelope;
@@ -102,13 +72,9 @@ export interface AnchorTraceBody {
102
72
  events: TraceEvent[];
103
73
  }
104
74
  /**
105
- * One sub-tree of a stamp, as the WITNESSED bytes carry it.
106
- *
107
- * Keyed in the body by a blinded reference (an HMAC under a per-org secret we
108
- * never publish), so a reader learns that the org runs N agents and their
109
- * relative volumes and learns no name. The RANGE is deliberately in the clear:
110
- * it is what makes a later per-agent slice claim checkable by a third party
111
- * without Sanning, and hiding it would put us back in the trust path.
75
+ * One sub-tree of a stamp, as the witnessed bytes carry it. Keyed by a blinded
76
+ * reference, so a reader learns no agent name. The range is in the clear so a
77
+ * per-agent slice claim is checkable without Sanning.
112
78
  */
113
79
  export interface StampSubtree {
114
80
  first_leaf: number;
@@ -117,15 +83,12 @@ export interface StampSubtree {
117
83
  root: string;
118
84
  }
119
85
  /**
120
- * The stamp body as the control plane witnesses it
121
- * (`control-plane/src/stamp-tree.ts`, `StampBody`).
122
- *
123
- * Never read from the bundle directly — it is parsed out of `stamp_bytes`,
124
- * which is the only authority. Present as a type so the parse has a shape.
86
+ * The stamp body as the control plane witnesses it. Only ever parsed out of
87
+ * `stamp_bytes`, the sole authority.
125
88
  */
126
89
  export interface StampBody {
127
90
  stamp_version: string;
128
- /** The org, BLINDED. Not an org id, and not reversible without the secret. */
91
+ /** The org, blinded. Not reversible without the secret. */
129
92
  org_ref: string;
130
93
  interval_start: string;
131
94
  interval_end: string;
@@ -135,18 +98,13 @@ export interface StampBody {
135
98
  }
136
99
  /** One entry of a `sanning.stamp.trace/v1` body's `stamps[]` (§5.2). */
137
100
  export interface StampEntry {
138
- /**
139
- * The stamp's identity within this bundle, DERIVED: hex SHA-256 of the
140
- * DECODED `stamp_bytes`, not of the hex text. A verifier MUST recompute it.
141
- */
101
+ /** Hex SHA-256 of the decoded `stamp_bytes`, not the hex text. Recomputed by the verifier. */
142
102
  stamp_ref: string;
143
103
  /** Lowercase hex of the exact JCS bytes that were handed to the witness. */
144
104
  stamp_bytes: string;
145
105
  /**
146
- * Where the stamp was witnessed, IF IT HAS BEEN (§5.1.1). Absent is honest:
147
- * a sealed-but-unconfirmed stamp is witnessed by nobody, and an emitter that
148
- * filled this in from a submission receipt would be claiming a chain
149
- * position that does not exist yet.
106
+ * Where the stamp was witnessed, if it has been (§5.1.1). Absent is honest: a
107
+ * submission receipt is not a chain position.
150
108
  */
151
109
  anchor_ref?: AnchorRef;
152
110
  }
@@ -159,7 +117,7 @@ export interface StampEvidenceInclusion {
159
117
  leaf_index: number;
160
118
  leaf_count: number;
161
119
  audit_path: string[];
162
- /** Resolves against `stamps[].stamp_ref` in the SAME body. */
120
+ /** Resolves against `stamps[].stamp_ref` in the same body. */
163
121
  stamp_ref: string;
164
122
  }
165
123
  export interface StampEvidenceEvent {
@@ -169,7 +127,7 @@ export interface StampEvidenceEvent {
169
127
  inclusion: StampEvidenceInclusion;
170
128
  }
171
129
  export interface StampEvidenceBody {
172
- /** Byte-identical to §5.2.2 — one definition, one place to change. */
130
+ /** Byte-identical to §5.2.2. */
173
131
  stamps: StampEntry[];
174
132
  events: StampEvidenceEvent[];
175
133
  }
@@ -179,7 +137,6 @@ export interface EvidenceBundle {
179
137
  issuer: EvidenceIssuer;
180
138
  generated_at: string;
181
139
  gateway?: string | null;
182
- verdict: EvidenceVerdict;
183
140
  body?: unknown;
184
141
  body_ref?: string;
185
142
  body_hash: string;
@@ -189,7 +146,7 @@ export interface EvidenceBundle {
189
146
  signature: string;
190
147
  [k: string]: unknown;
191
148
  }
192
- export type EvidenceStatus = "verified" | "partial" | "failed" | "malformed";
149
+ export type EvidenceStatus = "verified" | "failed" | "incomplete";
193
150
  export type OnChainOutcome = "confirm" | "mismatch" | "unreachable";
194
151
  export interface PerGatewayOutcome {
195
152
  gateway: string;
@@ -203,11 +160,7 @@ export interface OnChainResult {
203
160
  export interface CheckpointResult {
204
161
  /** The derived `checkpoint_ref`, empty when it could not be derived. */
205
162
  checkpointRef: string;
206
- /**
207
- * Where this checkpoint was witnessed. `null` when the checkpoint carries no
208
- * `anchor_ref`: placed but not yet witnessed, which is a normal state and NOT
209
- * a deficiency.
210
- */
163
+ /** Where it was witnessed; `null` when placed but not yet witnessed, which is normal. */
211
164
  anchorRef: AnchorRef | null;
212
165
  envelopeOk: boolean;
213
166
  merkleRootOk: boolean;
@@ -215,6 +168,7 @@ export interface CheckpointResult {
215
168
  onChain: OnChainResult | null;
216
169
  ok: boolean;
217
170
  errors: string[];
171
+ anchorRefMalformed?: boolean;
218
172
  }
219
173
  export interface EventResult {
220
174
  eventId: string;
@@ -225,10 +179,45 @@ export interface EventResult {
225
179
  contentOk: boolean | null;
226
180
  ok: boolean;
227
181
  errors: string[];
182
+ /**
183
+ * The `sanning.agent` reading of this record (agent-events.md section 10), read with
184
+ * every other record of the pile it was verified in: the bundle, or every pack
185
+ * of a case. `null` where the record is not an agent record, or its envelope
186
+ * did not verify, or its record bytes did not travel or did not bind. It never
187
+ * moves `ok` or the bundle status; the pack grade reads it (section 5.4.5).
188
+ */
189
+ agent: AgentRecordReading | null;
190
+ /**
191
+ * True where the record's `stamp_ref` or `checkpoint_ref` names a stamp or checkpoint the
192
+ * pack does not hold: `witnessed` is not proven (`missing`), never disproven, whatever else
193
+ * about the record fails.
194
+ */
195
+ stampAbsent?: boolean;
196
+ /**
197
+ * True where the record carries a retired field name, or bytes that are present and cannot
198
+ * be read: a defect in the artifact that fails the record under a key that can be read.
199
+ */
200
+ ownFormFailed?: boolean;
201
+ /**
202
+ * True where the record's signed bytes bind to its `payload_hash` under a signature that
203
+ * holds and are not JSON: the producer broke the format it signed, so `follows_rules` is
204
+ * disproven and nothing was proven altered.
205
+ */
206
+ recordNotJson?: boolean;
207
+ /**
208
+ * True where the bytes this record's content was checked against came from the side
209
+ * input, which nobody signed: a mismatch then leaves `unchanged` not proven
210
+ * (`not_checked`), never disproven.
211
+ */
212
+ contentUnsigned?: boolean;
213
+ /** Why the record's envelope does not verify: the worst of `envelopeFaults`, or `null`. */
214
+ envelopeFault?: EnvelopeFault | null;
215
+ /** Every envelope fault found (`ENVELOPE_FAULTS`), worst first. */
216
+ envelopeFaults?: EnvelopeFault[];
228
217
  }
229
218
  /** One sub-tree's recomputed result (§5.2). */
230
219
  export interface StampSubtreeResult {
231
- /** The blinded key the body spells it under. Meaningless without the org's secret. */
220
+ /** The blinded key the body uses. */
232
221
  ref: string;
233
222
  firstLeaf: number;
234
223
  leafCount: number;
@@ -236,17 +225,13 @@ export interface StampSubtreeResult {
236
225
  }
237
226
  /** One `stamps[]` entry's result (§5.2). */
238
227
  export interface StampResult {
239
- /** The stamp's derived identity, as the bundle spells it. "" when unusable. */
228
+ /** The stamp's derived identity. "" when unusable. */
240
229
  stampRef: string;
241
230
  /** The asserted `stamp_ref` equals SHA-256 of the decoded `stamp_bytes`. */
242
231
  stampRefOk: boolean;
243
232
  /**
244
- * The witnessed bytes decoded, parsed, and re-canonicalized to themselves.
245
- *
246
- * The last clause is not pedantry. The plane stores these bytes as TEXT and
247
- * replays them verbatim precisely because a round-trip through a normalizing
248
- * serializer is not guaranteed to reproduce what was signed; a body that is
249
- * not JCS-canonical is one that every re-deriving reader hashes differently.
233
+ * The witnessed bytes decode, parse and re-canonicalize to themselves. A body
234
+ * that is not JCS-canonical hashes differently for every re-deriving reader.
250
235
  */
251
236
  bodyOk: boolean;
252
237
  stampVersion: string | null;
@@ -255,55 +240,34 @@ export interface StampResult {
255
240
  merkleRoot: string | null;
256
241
  leafCount: number | null;
257
242
  /**
258
- * Every sub-tree range accounts for exactly one leaf of the stamp: the
259
- * ranges sorted by `first_leaf` tile `[0, leaf_count)` with no gap and no
260
- * overlap, and their counts sum to `leaf_count`.
243
+ * The sub-tree ranges, sorted by `first_leaf`, tile `[0, leaf_count)` with no
244
+ * gap or overlap.
261
245
  */
262
246
  subtreesOk: boolean;
263
247
  subtrees: StampSubtreeResult[];
264
248
  /**
265
- * Where it was witnessed, or `null` for a stamp nothing has witnessed yet.
266
- *
267
- * `null` is NOT a failure and never an accusation — it is the honest state
268
- * of every stamp for the first minutes of its life and the permanent state
269
- * of every stamp when no witness is configured. It does hold the bundle at
270
- * `partial`: a stamp is Sanning's own assertion until something independent
271
- * carries it.
249
+ * Where it was witnessed, or `null` if nothing has witnessed it yet. Never an
250
+ * accusation, but it holds the bundle at `partial`: until witnessed, a stamp
251
+ * is Sanning's own assertion.
272
252
  */
273
253
  anchorRef: AnchorRef | null;
274
254
  /**
275
- * Per-gateway on-chain outcomes for this stamp's `anchor_ref` (§4.2's shape,
276
- * `checkpoints[]`'s vocabulary), or `null` when NOTHING WAS CHECKED.
277
- *
278
- * ── The four states, and why none of them collapses ──
279
- *
280
- * `null` means not checked — no gateways were supplied, or this stamp names
281
- * no witness to check, or its `anchor_ref.kind` has no witness adapter, or
282
- * its id is not one the adapter accepts; the witness was not checked. It is
283
- * a statement about the RUN, never about the stamp. The other three are
284
- * statements about the stamp and live in `onChain.rollup`: `confirm` (a
285
- * gateway returned the witnessed bytes), `mismatch` (a gateway returned
286
- * DIFFERENT bytes: a finding, and the only one of the four that fails
287
- * anything), `unreachable` (we asked and nobody answered: an availability
288
- * gap, never an accusation).
289
- *
290
- * Keeping "not checked" distinct from "unreachable" is the whole point. A
291
- * surface handed a collapsed boolean cannot tell "no gateway could be
292
- * reached" from "no gateway was tried", and reported the first when the
293
- * second was true — a false accusation manufactured by a missing state.
255
+ * Per-gateway outcomes for this stamp's `anchor_ref`, or `null` when nothing
256
+ * was checked (a statement about the run, not the stamp). Of `confirm`,
257
+ * `mismatch` and `unreachable`, only `mismatch` fails anything. "Not checked"
258
+ * and "unreachable" stay distinct so a surface never reports an availability
259
+ * gap that was never tested.
294
260
  */
295
261
  onChain: OnChainResult | null;
296
262
  /**
297
- * Derived collapse of `onChain`, mirroring `CheckpointResult.onChainOk` so a
298
- * consumer reads one field the same way on both: `false` on mismatch, `true`
299
- * on confirm, `null` on unreachable AND on not-checked.
300
- *
301
- * It is deliberately the LOSSY view — `null` is three-quarters of the state
302
- * space here. Anything that needs to tell those apart reads `onChain`.
263
+ * Lossy collapse of `onChain`, as on `CheckpointResult`: `false` mismatch,
264
+ * `true` confirm, `null` unreachable or not checked.
303
265
  */
304
266
  onChainOk: boolean | null;
305
267
  ok: boolean;
306
268
  errors: string[];
269
+ /** True where the only thing wrong is a malformed `anchor_ref` (as on a checkpoint). */
270
+ anchorRefMalformed?: boolean;
307
271
  }
308
272
  export interface EvidenceBundleResult {
309
273
  status: EvidenceStatus;
@@ -314,10 +278,13 @@ export interface EvidenceBundleResult {
314
278
  onChainChecked: boolean;
315
279
  checkpoints: CheckpointResult[];
316
280
  events: EventResult[];
317
- assertedStatus: string | null;
318
281
  export?: ExportResult;
319
282
  stamps?: StampResult[];
320
283
  errors: string[];
284
+ unreadable?: boolean;
285
+ generatedAtMalformed?: boolean;
286
+ keyUnreadable?: boolean;
287
+ versionUnknown?: boolean;
321
288
  notes?: string[];
322
289
  }
323
290
  export interface AttestationRecord {
@@ -414,6 +381,7 @@ export interface AttestationResult {
414
381
  subjectRefOk: boolean | null;
415
382
  ok: boolean;
416
383
  errors: string[];
384
+ fieldError?: string | null;
417
385
  }
418
386
  export interface ExportResult {
419
387
  sourceLinkageOk: boolean;
@@ -422,6 +390,7 @@ export interface ExportResult {
422
390
  attestations: AttestationResult[];
423
391
  verdict: VerdictObject;
424
392
  status: EvidenceStatus;
393
+ sourceUnreadable?: boolean;
425
394
  }
426
395
  export interface VerifyEvidenceOptions {
427
396
  gateways?: string[];
@@ -429,40 +398,35 @@ export interface VerifyEvidenceOptions {
429
398
  fetchImpl?: typeof fetch;
430
399
  }
431
400
  /**
432
- * A surface MUST NOT present a wrapper's `co_signatures` as evidence
433
- * (evidence-bundle.md §2, decided by Phil 2026-09-24 on proof#148): nothing
434
- * in either kernel verifies the field, and it sits outside the wrapper's
435
- * signed scope (`bundleForSig` below), so anyone holding a pack's bytes can
436
- * append it with no key at all. This is the one sentence both kernels attach
437
- * as a note, never a verdict, never an exit code.
401
+ * A wrapper's `co_signatures` is not evidence (evidence-bundle.md §2): nothing
402
+ * verifies it, and it sits outside the signed scope, so anyone can append it.
403
+ * Attached as a note, never a verdict or exit code.
438
404
  */
439
405
  export declare const CO_SIGNATURES_NOTE = "unverified co-signatures are present in this pack's wrapper and are not part of the evidence";
440
406
  /**
441
- * True iff the wrapper's reserved `co_signatures` field carries at least one
442
- * entry.
443
- *
444
- * Absence and an empty array read the same way here: the field asserts
445
- * nothing about a second signer until it holds one, and evidence-bundle.md's
446
- * own carve-out language for the analogous envelope field treats "absent"
447
- * and "an empty array" as one thing (envelope-spec.md §7.1). A non-array
448
- * value (malformed shape, but the field is reserved, not yet specified)
449
- * still counts as present: it is not the well-formed absence either rule
450
- * describes.
407
+ * Whether the wrapper's reserved `co_signatures` carries at least one entry.
408
+ * Absent and empty read the same (envelope-spec.md §7.1); a non-array value
409
+ * counts as present.
451
410
  */
452
411
  export declare function coSignaturesPresent(b: unknown): boolean;
412
+ /** evidence-bundle.md §2. Byte-identical to the Python kernel's. */
413
+ export declare const GENERATED_AT_MALFORMED = "malformed generated_at: not an RFC 3339 UTC date-time with Z naming a real date and time (evidence-bundle section 2)";
453
414
  /**
454
- * A bundle this kernel cannot read.
455
- *
456
- * `wrapperOk` is true only where the wrapper signature and `body_hash` were
457
- * CHECKED and held before the body was found unreadable. It is then reported
458
- * as held, because it did: `signatureOk` and `bodyHashOk` are statements about
459
- * the wrapper, and a body this kernel cannot parse does not unsign it. The
460
- * case reader decides whose pack a bundle is on exactly those two fields
461
- * (`evidence-bundle.md` 5.5.1), so reporting `false` here let a holder who
462
- * broke its own body shape read as a forger.
415
+ * Name an envelope `public_key` or `signature` spelled outside the hex charset
416
+ * or at the wrong length, in the same words as Python, so both kernels give one
417
+ * reading. Only a string is checked; a missing one is the envelope layer's to
418
+ * fail.
419
+ */
420
+ /**
421
+ * The envelope faults that leave a claim not proven rather than disproven: the record's
422
+ * envelope or key cannot be read, or its version is one this verifier does not know.
423
+ */
424
+ export declare const UNPROVEN_ENVELOPE_FAULTS: readonly EnvelopeFault[];
425
+ /**
426
+ * Verify a `sanning.evidence/v1` bundle, dispatching on its `body_type`. The
427
+ * returned `status` is recomputed from the body; a wrapper `verdict` member,
428
+ * where a producer still writes one, is never read (evidence-bundle.md section 4).
463
429
  */
464
- /** evidence-bundle §2 (A31). Byte-identical to the Python kernel's. */
465
- export declare const GENERATED_AT_MALFORMED = "malformed generated_at: not an RFC 3339 UTC date-time with Z naming a real date and time (evidence-bundle \u00A72)";
466
430
  export declare function verifyEvidenceBundle(bundle: unknown, options?: VerifyEvidenceOptions): Promise<EvidenceBundleResult>;
467
431
  /**
468
432
  * The `anchor_ref.kind` a sandbox plane writes for a witness it simulated
@@ -485,18 +449,13 @@ export interface DisclosedContentResult {
485
449
  errors: string[];
486
450
  }
487
451
  /**
488
- * Materialize the disclosed raw bytes of a received bundle (evidence-bundle §5.1).
489
- *
490
- * Verify-then-return: the bundle is verified with {@link verifyEvidenceBundle}
491
- * (same options — gateways, out-of-band `content`, `fetchImpl`), and an event's
492
- * bytes are returned ONLY when that verification bound them to the committed
493
- * `content_hash`. There is no path through this function that yields bytes
494
- * whose SHA-256 is not the commitment.
452
+ * Materialize the disclosed raw bytes of a received bundle (evidence-bundle.md
453
+ * §5.1). The bundle is verified with {@link verifyEvidenceBundle}, and an
454
+ * event's bytes are returned only when that verification bound them to the
455
+ * committed `content_hash`.
495
456
  *
496
- * Returns `Uint8Array`, never a string: the spec commits to raw bytes, and a
497
- * UTF-8 decode is lossy for any payload that is not valid UTF-8 — re-encoding
498
- * such a string would produce different bytes and a different hash. Callers who
499
- * know their payload is text decode it themselves and own that assumption.
457
+ * Returns `Uint8Array`, never a string: a UTF-8 decode is lossy and would
458
+ * change the hash.
500
459
  */
501
460
  export declare function disclosedContent(bundle: unknown, options?: VerifyEvidenceOptions): Promise<DisclosedContentResult>;
502
461
  export declare function resolveTraceEvents(bundle: unknown): TraceEvent[] | undefined;