@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.
- package/README.md +30 -29
- package/dist/agent-record.d.ts +62 -0
- package/dist/agent-record.js +527 -0
- package/dist/agent-schemas.generated.d.ts +15 -0
- package/dist/agent-schemas.generated.js +5759 -0
- package/dist/case-file.d.ts +32 -89
- package/dist/case-file.js +41 -93
- package/dist/case.d.ts +339 -822
- package/dist/case.js +667 -1160
- package/dist/chain.d.ts +19 -47
- package/dist/chain.js +78 -205
- package/dist/claims.d.ts +211 -0
- package/dist/claims.js +1081 -0
- package/dist/cli.d.ts +45 -68
- package/dist/cli.js +618 -1022
- package/dist/compose.js +23 -65
- package/dist/crypto.d.ts +7 -8
- package/dist/crypto.js +60 -147
- package/dist/declaration.d.ts +68 -194
- package/dist/declaration.js +175 -518
- package/dist/environment.d.ts +9 -30
- package/dist/environment.js +17 -66
- package/dist/evidence.d.ts +119 -160
- package/dist/evidence.js +744 -1103
- package/dist/exit-codes.d.ts +11 -27
- package/dist/exit-codes.js +24 -59
- package/dist/file-match.d.ts +33 -79
- package/dist/file-match.js +33 -69
- package/dist/grading.d.ts +98 -135
- package/dist/grading.js +224 -380
- package/dist/handoff.d.ts +62 -160
- package/dist/handoff.js +153 -393
- package/dist/index.d.ts +14 -10
- package/dist/index.js +60 -167
- package/dist/json-schema.d.ts +29 -0
- package/dist/json-schema.js +298 -0
- package/dist/merkle.js +16 -28
- package/dist/message-parts.d.ts +56 -212
- package/dist/message-parts.js +142 -502
- package/dist/side-content.d.ts +11 -22
- package/dist/side-content.js +22 -65
- package/dist/types.d.ts +17 -0
- package/dist/types.js +19 -5
- package/dist/verifier.d.ts +40 -114
- package/dist/verifier.js +196 -535
- package/dist/witness-cache.d.ts +24 -45
- package/dist/witness-cache.js +38 -81
- package/dist/witness-lookup.d.ts +36 -57
- package/dist/witness-lookup.js +89 -139
- package/dist/zip-reader.d.ts +18 -33
- package/dist/zip-reader.js +19 -34
- package/package.json +11 -3
- package/src/agent-record.ts +608 -0
- package/src/agent-schemas.generated.ts +5764 -0
- package/src/case-file.ts +45 -109
- package/src/case.ts +814 -1328
- package/src/chain.ts +85 -224
- package/src/claims.ts +1253 -0
- package/src/cli.ts +714 -1176
- package/src/compose.ts +33 -79
- package/src/crypto.ts +60 -147
- package/src/declaration.ts +219 -656
- package/src/environment.ts +17 -65
- package/src/evidence.ts +958 -1381
- package/src/exit-codes.ts +24 -63
- package/src/file-match.ts +40 -89
- package/src/grading.ts +287 -421
- package/src/handoff.ts +175 -427
- package/src/index.ts +131 -177
- package/src/json-schema.ts +295 -0
- package/src/merkle.ts +16 -28
- package/src/message-parts.ts +155 -533
- package/src/side-content.ts +25 -68
- package/src/types.ts +55 -36
- package/src/verifier.ts +202 -526
- package/src/witness-cache.ts +45 -94
- package/src/witness-lookup.ts +107 -177
- package/src/zip-reader.ts +20 -38
- package/vectors/README.md +52 -34
- package/vectors/agent-record.json +2175 -0
- package/vectors/case-file-index.json +1 -1
- package/vectors/case-split.json +2 -2
- package/vectors/chain-reading.json +3 -3
- package/vectors/file-match.json +285 -148
- package/vectors/jcs-reader.json +6 -6
- package/vectors/pack-grading.json +102 -25
- package/vectors/sandbox-evidence.json +573 -581
- package/vectors/stamp-on-chain.json +18 -18
- package/vectors/witness-cache.json +49 -49
- package/dist/agent-proof.d.ts +0 -28
- package/dist/agent-proof.js +0 -226
- package/dist/run-adapters.d.ts +0 -10
- package/dist/run-adapters.js +0 -35
- package/dist/run.d.ts +0 -209
- package/dist/run.js +0 -1405
- package/src/agent-proof.ts +0 -264
- package/src/run-adapters.ts +0 -44
- package/src/run.ts +0 -1598
package/dist/handoff.d.ts
CHANGED
|
@@ -1,50 +1,28 @@
|
|
|
1
|
-
export declare const CANDIDATE_NAMESPACE = "handoff.";
|
|
2
|
-
export declare const DEFINED_EVENT_TYPES: readonly ["handoff.sent", "handoff.received"];
|
|
1
|
+
export declare const CANDIDATE_NAMESPACE = "agent.handoff.";
|
|
2
|
+
export declare const DEFINED_EVENT_TYPES: readonly ["agent.handoff.sent", "agent.handoff.received"];
|
|
3
3
|
/**
|
|
4
|
-
* The finding a record that is not a hand-off record at all
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* (anchor#188) and this reading names an outcome beside one, exactly as
|
|
8
|
-
* `counterpart_absent`, `counterpart_ambiguous` and `malformed_record` do. It
|
|
9
|
-
* exists because 4.1 had a SILENT branch, and a rule with a silent branch is a
|
|
10
|
-
* rule the party being audited steers into (proof#136). A record TRYING to be
|
|
11
|
-
* a hand-off and getting it wrong reads `malformed`; a record that is not a
|
|
12
|
-
* hand-off at all reads this. Both are readings. Neither is silence.
|
|
4
|
+
* The finding for a record that is not a hand-off record at all. A finding
|
|
5
|
+
* beside a verdict, not a verdict: a record that tries to be a hand-off and
|
|
6
|
+
* gets it wrong reads `malformed` or `nonconforming` instead. Neither is silence.
|
|
13
7
|
*/
|
|
14
8
|
export declare const NOT_A_HANDOFF_FINDING = "not_a_handoff_record";
|
|
15
9
|
/**
|
|
16
|
-
* An envelope `public_key` as a
|
|
17
|
-
*
|
|
18
|
-
* hex and so names no key this reading can compare.
|
|
10
|
+
* An envelope `public_key` as a key: the 32 key bytes as lowercase hex, or
|
|
11
|
+
* `null` where the value is not 64 hex characters.
|
|
19
12
|
*
|
|
20
|
-
* Case is
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* Python's `bytes.fromhex` skips ASCII whitespace and this kernel's charset
|
|
25
|
-
* check does not, a live divergence at the ENVELOPE layer and a separate
|
|
26
|
-
* founder call (proof#135). A reading rule that silently repaired it would
|
|
27
|
-
* hide it.
|
|
13
|
+
* Case is folded because the envelope layer accepts either case and the
|
|
14
|
+
* audited party is the producer. Whitespace is not stripped: Python's
|
|
15
|
+
* `bytes.fromhex` skips it and this kernel does not, and repairing that here
|
|
16
|
+
* would hide the divergence.
|
|
28
17
|
*/
|
|
29
18
|
export declare function normalizedSigningKey(value: unknown): string | null;
|
|
30
19
|
/**
|
|
31
|
-
* Whether two envelope keys are the
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* Where either side names no key, the question cannot be answered from key
|
|
35
|
-
* bytes and the answer is the conservative one: the two are reported as one
|
|
36
|
-
* key, so the pair reads `failed[self_handoff]` rather than clean. Exact
|
|
37
|
-
* string equality was the answer here and it was the wrong way round: one key
|
|
38
|
-
* that signed both ends, spelled `abcd...` on one envelope and `abcd ...` on
|
|
39
|
-
* the other, is one party, and string equality called it two and read the
|
|
40
|
-
* self-hand-off `verified` (proof#133 fourth hunt, finding 1). A record whose
|
|
41
|
-
* own envelope key this reading cannot read is `malformed` under
|
|
42
|
-
* `recordIsMalformed` and a formed pair reaches `malformed` before it reaches
|
|
43
|
-
* this line, so the two rules are belt and braces rather than one rule stated
|
|
44
|
-
* twice.
|
|
20
|
+
* Whether two envelope keys are the same key. Where either side names no key,
|
|
21
|
+
* the answer is the conservative one (same key), so the pair reads
|
|
22
|
+
* `failed[self_handoff]` rather than clean.
|
|
45
23
|
*/
|
|
46
24
|
export declare function sameSigningKey(left: unknown, right: unknown): boolean;
|
|
47
|
-
export type HandoffVerdict = "verified" | "failed" | "
|
|
25
|
+
export type HandoffVerdict = "verified" | "failed" | "incomplete";
|
|
48
26
|
export interface HandoffRecord {
|
|
49
27
|
event_type?: unknown;
|
|
50
28
|
event?: unknown;
|
|
@@ -53,87 +31,49 @@ export interface HandoffRecord {
|
|
|
53
31
|
[key: string]: unknown;
|
|
54
32
|
}
|
|
55
33
|
/**
|
|
56
|
-
* One hand-off record as a reader holds it: the committed record
|
|
57
|
-
* `public_key` of the envelope that carries it. The key comes from the
|
|
58
|
-
*
|
|
34
|
+
* One hand-off record as a reader holds it: the committed record plus the
|
|
35
|
+
* `public_key` of the envelope that carries it. The key comes from the
|
|
36
|
+
* envelope, never from the record.
|
|
59
37
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* every reading in this module is then worth exactly as much as that
|
|
64
|
-
* assertion. A reader builds a `HeldRecord` only from an envelope that
|
|
65
|
-
* `verifyEnvelope` returned `ok` for, bound to the canonical bytes of this
|
|
66
|
-
* record. Nothing in this module can check that for itself, which is why it is
|
|
67
|
-
* stated here (proof#133 adversarial gate, ruling 3), and why candidacy and
|
|
68
|
-
* the self-hand-off check read `normalizedSigningKey(publicKey)` rather than
|
|
69
|
-
* the spelling the audited party chose.
|
|
38
|
+
* Precondition: build a `HeldRecord` only from an envelope `verifyEnvelope`
|
|
39
|
+
* returned `ok` for. An unverified key is an assertion like any other, and this
|
|
40
|
+
* module cannot check it.
|
|
70
41
|
*/
|
|
71
42
|
export interface HeldRecord {
|
|
72
43
|
publicKey: string;
|
|
73
44
|
record: HandoffRecord;
|
|
74
45
|
ref: string;
|
|
46
|
+
/**
|
|
47
|
+
* The agent reader read this record `failed` (agent-events.md section
|
|
48
|
+
* 10). Set only by a reader that read the record with the pile it is held in
|
|
49
|
+
* (the case reading); the section 4.1 reading then takes the record as
|
|
50
|
+
* nonconforming, so its counterpart reads its candidate as nonconforming.
|
|
51
|
+
*/
|
|
52
|
+
agentNonconforming?: boolean;
|
|
75
53
|
}
|
|
76
54
|
/**
|
|
77
|
-
* Whether a
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* `handoff.` prefix did not deliver that: `HANDOFF.RECEIVED`,
|
|
81
|
-
* `sanning.handoff.received`, `handoff-received` and a non-string `event_type`
|
|
82
|
-
* all vanished from the reading entirely, not even as malformed, so a receipt
|
|
83
|
-
* typed one character outside the prefix was never read and, beside a clean decoy,
|
|
84
|
-
* published `verified` over proven tampering (proof#133 adversarial gate,
|
|
85
|
-
* ruling 2).
|
|
86
|
-
*
|
|
87
|
-
* The rule is checkable rather than a judgement, and it is exactly two
|
|
88
|
-
* clauses. The FIRST is the one the adversary cannot evade: the record's
|
|
89
|
-
* `event` carries `counterparty_public_key`, the field §4.1 defines and no
|
|
90
|
-
* other section does, WHATEVER its `event_type` says. The second keeps a
|
|
91
|
-
* record that omits the field in scope: an `event_type` that is a string whose
|
|
92
|
-
* folded form has `handoff` as one of its `.`-separated segments, which leaves
|
|
93
|
-
* `asset_missing` and the other families alone.
|
|
94
|
-
*
|
|
95
|
-
* The order is the fix, not decoration. Every earlier version of this test was
|
|
96
|
-
* an ALLOW-LIST OVER A NAME THE AUDITED PARTY WRITES, so each repair narrowed
|
|
97
|
-
* the class and the next spelling walked around it: an exact `handoff.` prefix
|
|
98
|
-
* fell to `HANDOFF.RECEIVED`, and the ASCII fold that replaced it fell to the
|
|
99
|
-
* `ff` ligature, full-width letters, a Cyrillic `a`, `hand-off`, `handoffs`, a
|
|
100
|
-
* zero-width space, a BOM and a non-breaking space, each of which left a
|
|
101
|
-
* tampered receipt with no reading at all beside a clean decoy (proof#133
|
|
102
|
-
* fourth hunt, finding 2). The structural field closes all of them in one rule
|
|
103
|
-
* rather than by chasing Unicode, because the attack needs the field: a
|
|
104
|
-
* receipt that drops `counterparty_public_key` says nothing about who it came
|
|
105
|
-
* from.
|
|
55
|
+
* Whether a record is in the hand-off namespace: its `event` carries
|
|
56
|
+
* `counterparty_public_key` (the field §4.1 defines), whatever its `event_type`
|
|
57
|
+
* says, or its folded `event_type` has `handoff` as a `.`-separated segment.
|
|
106
58
|
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* by the party being audited, and one code point on each put a record in scope
|
|
112
|
-
* by neither, which produced silence. `readHandoffs` reads every record it
|
|
113
|
-
* holds, and this test now decides only WHICH reading a record gets: in scope
|
|
114
|
-
* and wrong reads `malformed`, out of scope reads `NOT_A_HANDOFF_FINDING`. A
|
|
115
|
-
* respelling buys a different line rather than no line.
|
|
59
|
+
* The structural clause comes first because a name is written by the audited
|
|
60
|
+
* party and Unicode respellings walk around any allow-list. This decides only
|
|
61
|
+
* which reading a record gets: in scope and wrong reads `malformed`, out of
|
|
62
|
+
* scope reads `NOT_A_HANDOFF_FINDING`.
|
|
116
63
|
*/
|
|
117
64
|
export declare function isHandoffNamespace(held: HeldRecord): boolean;
|
|
118
65
|
/**
|
|
119
|
-
* What one candidate graded to, kept beside the aggregate
|
|
120
|
-
*
|
|
121
|
-
* `failed`, so one junk record signed by the counterparty key would otherwise
|
|
122
|
-
* turn a named `message_hash` failure into a `malformed` with nothing named
|
|
123
|
-
* (proof#133 review, ruling 6).
|
|
66
|
+
* What one candidate graded to, kept beside the aggregate, so a junk record
|
|
67
|
+
* that outranks it (`malformed` over `failed`) cannot hide a named failure.
|
|
124
68
|
*/
|
|
125
69
|
export interface CandidateReading {
|
|
126
70
|
ref: string;
|
|
127
71
|
verdict: HandoffVerdict;
|
|
128
72
|
failedChecks: string[];
|
|
129
73
|
/**
|
|
130
|
-
* The ref of the record that
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* reported here and takes no part in the subject's verdict, failed checks or
|
|
134
|
-
* ambiguity. Every reading this kernel returns carries it. It is optional in
|
|
135
|
-
* the type only so that code which builds a `CandidateReading` itself keeps
|
|
136
|
-
* compiling: the field is an addition.
|
|
74
|
+
* The ref of the record that accounts for this failed candidate, or `null`.
|
|
75
|
+
* An accounted-for candidate takes no part in the subject's verdict, failed
|
|
76
|
+
* checks or ambiguity. Optional only so caller-built readings compile.
|
|
137
77
|
*/
|
|
138
78
|
accountedFor?: string | null;
|
|
139
79
|
}
|
|
@@ -146,79 +86,41 @@ export interface HandoffReading {
|
|
|
146
86
|
/** Ordered, never filtered. */
|
|
147
87
|
candidates: string[];
|
|
148
88
|
/**
|
|
149
|
-
* One entry per candidate, in the same order
|
|
150
|
-
*
|
|
151
|
-
* where every candidate is accounted for the reading is `counterpart_absent`
|
|
152
|
-
* with the candidates still listed.
|
|
89
|
+
* One entry per candidate, in the same order. Where every candidate is
|
|
90
|
+
* accounted for, the reading is `counterpart_absent` with them still listed.
|
|
153
91
|
*/
|
|
154
92
|
candidateReadings: CandidateReading[];
|
|
155
93
|
}
|
|
156
94
|
/**
|
|
157
|
-
*
|
|
158
|
-
* not the type §4.1 states, is malformed and fails. Stated as a rule because
|
|
159
|
-
* §4.1 had none: a pair where both sides omitted `message_hash` compared absent
|
|
160
|
-
* to absent, found them equal, and graded intact (proof#132).
|
|
95
|
+
* What is wrong with a record section 4.1 governs, read on its own, or `null`.
|
|
161
96
|
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
* whitespace, which reopened the whole of the first attack through the repair
|
|
170
|
-
* rather than around it (proof#133 fourth hunt, finding 1). A record a reader
|
|
171
|
-
* HOLDS, whose own key it cannot read, is a defect in the record and not an
|
|
172
|
-
* absence of one. This holds whatever the envelope layer decides about its hex
|
|
173
|
-
* charset (proof#135).
|
|
97
|
+
* `malformed` where it cannot be read as a hand-off record: its envelope key
|
|
98
|
+
* does not parse (an unreadable key would drop it out of candidacy and the
|
|
99
|
+
* other side would read an innocent `counterpart_absent`), or its type is a
|
|
100
|
+
* respelling of the two names. `nonconforming` where it is one of the two types
|
|
101
|
+
* and a required field is missing or mistyped (two records that both omit
|
|
102
|
+
* `message_hash` would otherwise compare equal), or the agent reader read it
|
|
103
|
+
* `failed` (`agentNonconforming`).
|
|
174
104
|
*/
|
|
105
|
+
export declare function recordDefect(held: HeldRecord): "malformed" | "nonconforming" | null;
|
|
106
|
+
/** `recordDefect` is `malformed`. */
|
|
175
107
|
export declare function recordIsMalformed(held: HeldRecord): boolean;
|
|
108
|
+
/** `recordDefect` is `nonconforming`. */
|
|
109
|
+
export declare function recordIsNonconforming(held: HeldRecord): boolean;
|
|
176
110
|
/**
|
|
177
|
-
* Read
|
|
178
|
-
*
|
|
179
|
-
* Every record gets a reading, and the two findings are how the distinction a
|
|
180
|
-
* reader needs survives (proof#136): a record that is not a hand-off record at
|
|
181
|
-
* all reads `NOT_A_HANDOFF_FINDING` with no verdict, and a record 4.1 DOES
|
|
182
|
-
* govern and that is wrong reads `malformed`. The scope test lives HERE and
|
|
183
|
-
* not in `readHandoffs`, so the two entry points cannot answer differently
|
|
184
|
-
* about one record.
|
|
111
|
+
* Read one record against everything the reader holds. The scope test lives
|
|
112
|
+
* here, not in `readHandoffs`, so the two entry points cannot disagree.
|
|
185
113
|
*/
|
|
186
114
|
export declare function readHandoff(subject: HeldRecord, held: readonly HeldRecord[]): HandoffReading;
|
|
187
115
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
* record was read at all is gone (proof#136, ratified by Phil 2026-09-22).
|
|
192
|
-
* Scope was two clauses, an exact test for the key name
|
|
193
|
-
* `counterparty_public_key` and an ASCII fold of `event_type`, and both
|
|
194
|
-
* operands are written by the party being audited: one code point on each put
|
|
195
|
-
* a record in scope by NEITHER route, so it had no reading at all and the pile
|
|
196
|
-
* read clean. Five repairs narrowed which values reached that silent branch
|
|
197
|
-
* and five hunts found a value outside it, so the branch is removed rather
|
|
198
|
-
* than narrowed again.
|
|
199
|
-
*
|
|
200
|
-
* The distinction a reader needs survives as two findings rather than as a
|
|
201
|
-
* reading and a silence: a record TRYING to be a hand-off and getting it wrong
|
|
202
|
-
* reads `malformed` with `malformed_record`, and a record that is not a
|
|
203
|
-
* hand-off record at all reads `NOT_A_HANDOFF_FINDING` with no verdict.
|
|
204
|
-
* `isHandoffNamespace` decides which of the two, in `readHandoff`, and no
|
|
205
|
-
* longer whether a record is read.
|
|
206
|
-
*
|
|
207
|
-
* THE COST, stated rather than left to be found: a reader given a mixed pile
|
|
208
|
-
* produces a line per record, including for records that have nothing to do
|
|
209
|
-
* with hand-offs. 4.1 says so too.
|
|
116
|
+
* Read every held record, in the order given. Nothing is filtered: a record
|
|
117
|
+
* outside the hand-off namespace reads `NOT_A_HANDOFF_FINDING`, so a mixed pile
|
|
118
|
+
* produces a line per record.
|
|
210
119
|
*/
|
|
211
120
|
export declare function readHandoffs(held: readonly HeldRecord[]): HandoffReading[];
|
|
212
121
|
/**
|
|
213
|
-
* Whether
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
* `handoff.sent` or a `handoff.received` carrying every REQUIRED field of its
|
|
217
|
-
* own type, under an envelope key this reading can read.
|
|
218
|
-
*
|
|
219
|
-
* `./declaration.ts` reads this for evidence-bundle.md 5.4: a claimed
|
|
220
|
-
* `sent_event_id` or `received_event_id` naming a record that is not one of
|
|
221
|
-
* these is a DEVIATION, so a record cannot be unreadable at the record layer
|
|
222
|
-
* and usable at the pack layer at the same time (proof#136).
|
|
122
|
+
* Whether a record is a conformant hand-off: in the namespace, with no
|
|
123
|
+
* defect. `./declaration.ts` uses it for evidence-bundle.md section 5.4, so a record
|
|
124
|
+
* cannot be unreadable here and usable at the pack layer.
|
|
223
125
|
*/
|
|
224
126
|
export declare function isConformantHandoffRecord(held: HeldRecord): boolean;
|