@sanning/proof 0.11.0 → 0.12.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 +22 -0
- package/dist/case.d.ts +25 -16
- package/dist/case.js +53 -23
- package/dist/cli.d.ts +98 -0
- package/dist/cli.js +701 -6
- package/dist/handoff.d.ts +16 -1
- package/dist/handoff.js +111 -17
- package/dist/index.js +2 -1
- package/package.json +1 -1
- package/src/case.ts +65 -24
- package/src/cli.ts +787 -7
- package/src/handoff.ts +129 -18
- package/src/index.ts +2 -1
- package/vectors/README.md +35 -0
- package/vectors/case-split.json +893 -0
package/README.md
CHANGED
|
@@ -163,6 +163,28 @@ Every event also appears in `result.events`, in body order, with a status that n
|
|
|
163
163
|
|
|
164
164
|
Bytes come back as `Uint8Array`, never a string: the spec commits to **raw bytes**, and a UTF-8 decode is lossy for any payload that is not valid UTF-8, re-encoding such a string would change the bytes and break the hash. Decode to text yourself if you know your payload is text.
|
|
165
165
|
|
|
166
|
+
## Check a case: several packs, one verdict
|
|
167
|
+
|
|
168
|
+
When a case spans more than one pack (two agents, two organizations, one hand-off between them), check the packs together. This is `readCase` from a terminal, with no page in the way:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npx @sanning/proof case assessor-pack/ challenger-pack/
|
|
172
|
+
unzip decision-file.zip -d case/ && npx @sanning/proof case case/ # a decision file
|
|
173
|
+
npx @sanning/proof case case/ --json # the full reading, for a script
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Each argument is a folder: one pack (`bundle.json` plus `logs/`), or a folder that holds several packs, such as an unpacked decision file. `case` does not open archives: given a `.zip`, it exits `2` and says to unzip it first and pass the folder. Packs are found by the verify page's splitting rule, so the page and this command apply the same rule to the same unpacked tree: every `bundle.json` is a pack, a file belongs to the deepest pack whose `bundle.json` or `logs/` it sits in, and every other file is listed as outside every pack and never opened. A `.zip` inside the folder is a file like any other. Several folders are read as one folder holding them, each under its own name. Each `bundle.json` is read with `verify`'s own reader (`readBundleBytes`), so the two commands classify one bundle the same way.
|
|
177
|
+
|
|
178
|
+
The walk is bounded and follows no link. A symbolic link is never followed: it is listed as outside every pack and not read, even under a pack's `logs/`, and a link named on the command line exits `2`. A folder nested more than 32 levels deep, more than 50,000 files and folders in all, or a name that is not valid UTF-8 exits `2`; a bad name is refused rather than escaped, because two different names would then read as one. A `bundle.json` nested more than 512 levels deep exits `2`, from `case` and from `verify` alike. The rules are pinned for both kernels by `vectors/case-split.json`, shipped in this tarball, as directory trees.
|
|
179
|
+
|
|
180
|
+
Known differences from the verify page: the page opens a dropped `.zip`; the page drops a leading byte-order mark from `bundle.json`, which `verify`'s reader refuses; the page reads whatever the browser hands it for a link, a bad name or a very deep tree; and the page loses a record named `__proto__`, which this command keeps.
|
|
181
|
+
|
|
182
|
+
The first line is the case verdict, then the kernel's summary and every exception through `renderException`, then the packs, the files outside them, and the on-chain half. Exit codes: `0` verified · `1` failed · `2` malformed, usage, an input that cannot be read as a case, or an unexpected error in the command · `3` incomplete or partial · `4` mixed (more than one outcome: read the exceptions). `verify` never exits `4`.
|
|
183
|
+
|
|
184
|
+
A case reading establishes that every pack verified offline, that every record every pack supplied was read in one pile under envelope-spec §4.1's hand-off rules, and that a hand-off record with no counterpart is always named. It does **not** establish that the case is complete, that a party which presented no pack under the named key recorded nothing, that two organizations are independent, or that any witness was re-fetched on chain: the output says `on-chain NOT CHECKED` unless you pass `--gateways <url,url>`. With gateways, each pack's witnesses are re-fetched pack by pack and reported beside the reading, which they never change; a witness that does not match exits `1`, and an on-chain half that could not be settled turns `0` into `3`.
|
|
185
|
+
|
|
186
|
+
The Python kernel ships the same command as `sanning-proof case`, and `cross-kernel/run_case_cli.sh` requires the two to print the same bytes, the same `--json` and the same exit code over every `case/` corpus vector and every tree in `vectors/case-split.json`.
|
|
187
|
+
|
|
166
188
|
## Verify a Merkle inclusion proof
|
|
167
189
|
|
|
168
190
|
Agents close daily checkpoints over per-cycle leaves (RFC 9162, §2.1 domain separation, not the Bitcoin duplicate-leaf variant). Verify a leaf's inclusion against an anchored root:
|
package/dist/case.d.ts
CHANGED
|
@@ -185,7 +185,8 @@
|
|
|
185
185
|
* (F2).
|
|
186
186
|
* 10. **`counterpart_absent`** (`continue`). Skips the ambiguity and pair
|
|
187
187
|
* reporting, and there is nothing to skip: 4.1 emits that finding only
|
|
188
|
-
*
|
|
188
|
+
* where no candidate is left to read, an EMPTY candidate list or one whose
|
|
189
|
+
* every candidate another record accounts for (4.1 v1.17, A20). Which of the three counterpart kinds it
|
|
189
190
|
* gets reads the keys of the packs that verified, counted before exit 4,
|
|
190
191
|
* and whether any pack took exit 2 or 3.
|
|
191
192
|
*
|
|
@@ -225,16 +226,16 @@
|
|
|
225
226
|
* with that verdict and its reasons, so the case never reads above its
|
|
226
227
|
* weakest pack. On-chain re-fetch and `gradePack` are a caller's to run.
|
|
227
228
|
*
|
|
228
|
-
* THE COST OF 4.1's "GRADE EVERY CANDIDATE",
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
229
|
+
* THE COST OF 4.1's "GRADE EVERY CANDIDATE", AND WHAT IS LEFT OF IT (4.1
|
|
230
|
+
* v1.17, amendment A20, which reopens proof#134). Up to v1.16 a pile holding
|
|
231
|
+
* two or more records of the complementary type signed by one counterparty key
|
|
232
|
+
* could not reach `verified`. 4.1 now sets such a candidate aside where a
|
|
233
|
+
* record the subject's own key signed pairs with it verified at another
|
|
234
|
+
* sequence, so two messages in one direction, and the demo's A to B, B to A,
|
|
235
|
+
* A to B, read clean. What is left surfaces as `COUNTERPART_AMBIGUOUS` beside
|
|
236
|
+
* the failure: fan-in, fan-out, and the same pair in two cases at the same
|
|
237
|
+
* sequence. Narrowing any of it here with `handoffs[]` is precisely the move
|
|
238
|
+
* that would reopen proof#137's door.
|
|
238
239
|
*/
|
|
239
240
|
import type { SideContentMap } from "./side-content.js";
|
|
240
241
|
/**
|
|
@@ -281,7 +282,9 @@ export declare const CASE_QUARANTINED_PROFILES: readonly ["sanning.mlflow/v1"];
|
|
|
281
282
|
/**
|
|
282
283
|
* A conformant hand-off record with no counterpart among the records this
|
|
283
284
|
* reader read, where every pack in the case verified under a key this reader
|
|
284
|
-
* can read and NO pack in the case is signed by the key the record names
|
|
285
|
+
* can read and NO pack in the case is signed by the key the record names,
|
|
286
|
+
* or where every candidate it had was accounted for by another record under
|
|
287
|
+
* A20 (the exception then carries `count`, and names no pack).
|
|
285
288
|
* **An exception, never a failure** (anchor#198, criterion 3).
|
|
286
289
|
*
|
|
287
290
|
* That is ALL it says. It does not say the counterparty's account is absent,
|
|
@@ -367,9 +370,11 @@ export declare const COUNTERPART_ABSENT_FROM_COUNTERPARTY_RECORDS = "counterpart
|
|
|
367
370
|
*/
|
|
368
371
|
export declare const COUNTERPART_ABSENT_SIGNER_UNKNOWN = "counterpart_absent_signer_unknown";
|
|
369
372
|
/**
|
|
370
|
-
* More than one candidate counterpart (4.1
|
|
371
|
-
* reading is the worst of them and the ambiguity is named: a reader
|
|
372
|
-
* to take one match can be handed an honest decoy beside a tampered
|
|
373
|
+
* More than one candidate counterpart that nothing accounts for (4.1 v1.17,
|
|
374
|
+
* A20). The reading is the worst of them and the ambiguity is named: a reader
|
|
375
|
+
* permitted to take one match can be handed an honest decoy beside a tampered
|
|
376
|
+
* record. `count` is how many of the candidates are live, which is the number
|
|
377
|
+
* the ambiguity is about.
|
|
373
378
|
*/
|
|
374
379
|
export declare const COUNTERPART_AMBIGUOUS = "counterpart_ambiguous";
|
|
375
380
|
/**
|
|
@@ -636,7 +641,11 @@ export interface CaseException {
|
|
|
636
641
|
counterpartyKey?: string | null;
|
|
637
642
|
/** Which checks a formed pair failed, in 4.1's fixed order. */
|
|
638
643
|
failedChecks?: string[];
|
|
639
|
-
/**
|
|
644
|
+
/**
|
|
645
|
+
* How many of a thing the exception is about, where it is about a count. On
|
|
646
|
+
* `COUNTERPART_ABSENT` it is set only where 4.1 set every candidate aside
|
|
647
|
+
* (v1.17, A20), and it is how many were set aside.
|
|
648
|
+
*/
|
|
640
649
|
count?: number;
|
|
641
650
|
/** The pack's own bundle verdict, for `PACK_BUNDLE_NOT_VERIFIED`. */
|
|
642
651
|
status?: CaseVerdict;
|
package/dist/case.js
CHANGED
|
@@ -185,7 +185,8 @@
|
|
|
185
185
|
* (F2).
|
|
186
186
|
* 10. **`counterpart_absent`** (`continue`). Skips the ambiguity and pair
|
|
187
187
|
* reporting, and there is nothing to skip: 4.1 emits that finding only
|
|
188
|
-
*
|
|
188
|
+
* where no candidate is left to read, an EMPTY candidate list or one whose
|
|
189
|
+
* every candidate another record accounts for (4.1 v1.17, A20). Which of the three counterpart kinds it
|
|
189
190
|
* gets reads the keys of the packs that verified, counted before exit 4,
|
|
190
191
|
* and whether any pack took exit 2 or 3.
|
|
191
192
|
*
|
|
@@ -225,16 +226,16 @@
|
|
|
225
226
|
* with that verdict and its reasons, so the case never reads above its
|
|
226
227
|
* weakest pack. On-chain re-fetch and `gradePack` are a caller's to run.
|
|
227
228
|
*
|
|
228
|
-
* THE COST OF 4.1's "GRADE EVERY CANDIDATE",
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
229
|
+
* THE COST OF 4.1's "GRADE EVERY CANDIDATE", AND WHAT IS LEFT OF IT (4.1
|
|
230
|
+
* v1.17, amendment A20, which reopens proof#134). Up to v1.16 a pile holding
|
|
231
|
+
* two or more records of the complementary type signed by one counterparty key
|
|
232
|
+
* could not reach `verified`. 4.1 now sets such a candidate aside where a
|
|
233
|
+
* record the subject's own key signed pairs with it verified at another
|
|
234
|
+
* sequence, so two messages in one direction, and the demo's A to B, B to A,
|
|
235
|
+
* A to B, read clean. What is left surfaces as `COUNTERPART_AMBIGUOUS` beside
|
|
236
|
+
* the failure: fan-in, fan-out, and the same pair in two cases at the same
|
|
237
|
+
* sequence. Narrowing any of it here with `handoffs[]` is precisely the move
|
|
238
|
+
* that would reopen proof#137's door.
|
|
238
239
|
*/
|
|
239
240
|
import { checkDeclaration, declarationClaims, packRecords, } from "./declaration.js";
|
|
240
241
|
import { NOT_A_HANDOFF_FINDING, normalizedSigningKey, readHandoffs, recordIsMalformed, } from "./handoff.js";
|
|
@@ -309,7 +310,9 @@ export const CASE_QUARANTINED_PROFILES = ["sanning.mlflow/v1"];
|
|
|
309
310
|
/**
|
|
310
311
|
* A conformant hand-off record with no counterpart among the records this
|
|
311
312
|
* reader read, where every pack in the case verified under a key this reader
|
|
312
|
-
* can read and NO pack in the case is signed by the key the record names
|
|
313
|
+
* can read and NO pack in the case is signed by the key the record names,
|
|
314
|
+
* or where every candidate it had was accounted for by another record under
|
|
315
|
+
* A20 (the exception then carries `count`, and names no pack).
|
|
313
316
|
* **An exception, never a failure** (anchor#198, criterion 3).
|
|
314
317
|
*
|
|
315
318
|
* That is ALL it says. It does not say the counterparty's account is absent,
|
|
@@ -395,9 +398,11 @@ export const COUNTERPART_ABSENT_FROM_COUNTERPARTY_RECORDS = "counterpart_absent_
|
|
|
395
398
|
*/
|
|
396
399
|
export const COUNTERPART_ABSENT_SIGNER_UNKNOWN = "counterpart_absent_signer_unknown";
|
|
397
400
|
/**
|
|
398
|
-
* More than one candidate counterpart (4.1
|
|
399
|
-
* reading is the worst of them and the ambiguity is named: a reader
|
|
400
|
-
* to take one match can be handed an honest decoy beside a tampered
|
|
401
|
+
* More than one candidate counterpart that nothing accounts for (4.1 v1.17,
|
|
402
|
+
* A20). The reading is the worst of them and the ambiguity is named: a reader
|
|
403
|
+
* permitted to take one match can be handed an honest decoy beside a tampered
|
|
404
|
+
* record. `count` is how many of the candidates are live, which is the number
|
|
405
|
+
* the ambiguity is about.
|
|
401
406
|
*/
|
|
402
407
|
export const COUNTERPART_AMBIGUOUS = "counterpart_ambiguous";
|
|
403
408
|
/**
|
|
@@ -1135,19 +1140,36 @@ export async function readCase(packs) {
|
|
|
1135
1140
|
// that party's pack. Where some pack could not be tied to a key, "no
|
|
1136
1141
|
// pack is signed by the named key" is not established and the reading
|
|
1137
1142
|
// says it could not tell.
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1143
|
+
//
|
|
1144
|
+
// EXCEPT where 4.1 set every candidate aside (v1.17, A20). There the
|
|
1145
|
+
// absence is a finding about the candidates the reader DID read, each
|
|
1146
|
+
// paired `verified` with another record the subject's own signer made,
|
|
1147
|
+
// and not a finding about any pack. 4.1 and evidence-bundle A16 name the
|
|
1148
|
+
// neutral kind for it, and the damning one would put the audited party's
|
|
1149
|
+
// own unfaithful record on the counterparty's pack as a statement
|
|
1150
|
+
// against it. `count` is how many candidates were set aside, which is
|
|
1151
|
+
// also what tells the rendered sentence which absence this is.
|
|
1152
|
+
const graded = reading.candidateReadings;
|
|
1153
|
+
const setAside = graded.length > 0 && graded.every((c) => c.accountedFor !== null)
|
|
1154
|
+
? graded.length
|
|
1155
|
+
: 0;
|
|
1156
|
+
const kind = setAside > 0
|
|
1157
|
+
? COUNTERPART_ABSENT
|
|
1158
|
+
: named !== null && holderKeys.has(named)
|
|
1159
|
+
? COUNTERPART_ABSENT_FROM_COUNTERPARTY_RECORDS
|
|
1160
|
+
: unattributed > 0
|
|
1161
|
+
? COUNTERPART_ABSENT_SIGNER_UNKNOWN
|
|
1162
|
+
: COUNTERPART_ABSENT;
|
|
1163
|
+
note(kind, setAside > 0
|
|
1164
|
+
? { packRef, eventId, counterpartyKey: named, count: setAside }
|
|
1165
|
+
: { packRef, eventId, counterpartyKey: named });
|
|
1144
1166
|
continue;
|
|
1145
1167
|
}
|
|
1146
1168
|
if (reading.findings.includes(COUNTERPART_AMBIGUOUS)) {
|
|
1147
1169
|
note(COUNTERPART_AMBIGUOUS, {
|
|
1148
1170
|
packRef,
|
|
1149
1171
|
eventId,
|
|
1150
|
-
count: reading.
|
|
1172
|
+
count: reading.candidateReadings.filter((c) => c.accountedFor === null).length,
|
|
1151
1173
|
});
|
|
1152
1174
|
}
|
|
1153
1175
|
// ON `failedChecks` AND NOT ON THE VERDICT WORD. 4.1 keeps the UNION of
|
|
@@ -1192,6 +1214,14 @@ export function renderException(exception) {
|
|
|
1192
1214
|
const where = `${exception.packRef}/${exception.eventId}`;
|
|
1193
1215
|
switch (exception.kind) {
|
|
1194
1216
|
case COUNTERPART_ABSENT:
|
|
1217
|
+
if (exception.count) {
|
|
1218
|
+
const which = exception.count === 1
|
|
1219
|
+
? "its one candidate counterpart pairs"
|
|
1220
|
+
: `each of its ${exception.count} candidate counterparts pairs`;
|
|
1221
|
+
return (`${where} has no counterpart among the records this verifier read: ` +
|
|
1222
|
+
`${which} verified with another record signed by the key that ` +
|
|
1223
|
+
"signed this one, at a different sequence, and is set aside");
|
|
1224
|
+
}
|
|
1195
1225
|
return (`${where} has no counterpart among the records this verifier read, ` +
|
|
1196
1226
|
"and no pack in this case is signed by the key it names");
|
|
1197
1227
|
case COUNTERPART_ABSENT_FROM_COUNTERPARTY_RECORDS:
|
|
@@ -1276,7 +1306,7 @@ export function renderException(exception) {
|
|
|
1276
1306
|
because(exception.reasons));
|
|
1277
1307
|
case RECORD_PROFILE_QUARANTINED:
|
|
1278
1308
|
return (`${where} is signed under a profile this case reader does not read ` +
|
|
1279
|
-
"while its admission is undecided
|
|
1309
|
+
"while its admission is undecided, so what it says is UNKNOWN");
|
|
1280
1310
|
case CASE_HOLDS_NO_PACKS:
|
|
1281
1311
|
return "this case holds no packs";
|
|
1282
1312
|
case CASE_HOLDS_NO_HANDOFF_RECORDS:
|
package/dist/cli.d.ts
CHANGED
|
@@ -5,4 +5,102 @@ interface Cli {
|
|
|
5
5
|
fetchImpl?: typeof fetch;
|
|
6
6
|
}
|
|
7
7
|
export declare function runCli(argv: string[], io: Cli): Promise<number>;
|
|
8
|
+
/** The error text for an input with no pack in it, word for word the page's. */
|
|
9
|
+
export declare const NO_BUNDLE = "No bundle.json found. An evidence pack contains bundle.json plus logs/.";
|
|
10
|
+
/** A directory nested deeper than this below an argument refuses the input. */
|
|
11
|
+
export declare const MAX_WALK_DEPTH = 32;
|
|
12
|
+
/** More names than this, across every argument, refuse the input. */
|
|
13
|
+
export declare const MAX_WALK_ENTRIES = 50000;
|
|
14
|
+
/**
|
|
15
|
+
* `bundle.json` nested deeper than this is refused before it is parsed, by
|
|
16
|
+
* `verify` and by `case` alike (see `readBundleBytes`).
|
|
17
|
+
*/
|
|
18
|
+
export declare const MAX_JSON_DEPTH = 512;
|
|
19
|
+
/**
|
|
20
|
+
* The input could not be read as a case. The message is the whole report,
|
|
21
|
+
* identical in both kernels, and the command exits 2 on it. `hint` is an
|
|
22
|
+
* optional second line naming a way through.
|
|
23
|
+
*/
|
|
24
|
+
export declare class CaseInputError extends Error {
|
|
25
|
+
readonly hint: string | null;
|
|
26
|
+
constructor(message: string, hint?: string | null);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* `bundle.json` bytes that `readBundleBytes` will not read. `tooDeep` separates
|
|
30
|
+
* the one refusal that is not a parse error; `detail` is the parser's own words,
|
|
31
|
+
* which differ by kernel and are printed by `verify` only.
|
|
32
|
+
*/
|
|
33
|
+
export declare class BundleReadError extends Error {
|
|
34
|
+
readonly tooDeep: boolean;
|
|
35
|
+
readonly detail: string;
|
|
36
|
+
constructor(tooDeep: boolean, detail: string);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* How both `verify` and `case` turn `bundle.json` bytes into JSON, in both
|
|
40
|
+
* kernels (`read_bundle_bytes` in the Python twin). UTF-8 with each ill-formed
|
|
41
|
+
* sequence read as U+FFFD, and a byte-order mark kept; then nesting deeper than
|
|
42
|
+
* MAX_JSON_DEPTH is refused before any parser walks into it; then `JSON.parse`.
|
|
43
|
+
* Throws `BundleReadError`.
|
|
44
|
+
*/
|
|
45
|
+
export declare function readBundleBytes(bytes: Uint8Array): unknown;
|
|
46
|
+
export interface SplitPack {
|
|
47
|
+
ref: string;
|
|
48
|
+
bundlePath: string;
|
|
49
|
+
bundleBytes: Uint8Array;
|
|
50
|
+
/**
|
|
51
|
+
* `logs/<event_id>.json` bytes by event id. A `Map` and not an object: an
|
|
52
|
+
* event id is written by the audited party, and `obj["__proto__"] = bytes`
|
|
53
|
+
* replaces the prototype instead of storing the disclosure.
|
|
54
|
+
*/
|
|
55
|
+
content: Map<string, Uint8Array>;
|
|
56
|
+
unaccounted: string[];
|
|
57
|
+
}
|
|
58
|
+
export interface SplitCase {
|
|
59
|
+
packs: SplitPack[];
|
|
60
|
+
/** Paths that belong to no pack, sorted. Never opened. */
|
|
61
|
+
outside: string[];
|
|
62
|
+
/** The part of `outside` that is not a regular file. Never followed, never opened. */
|
|
63
|
+
notRegular: string[];
|
|
64
|
+
}
|
|
65
|
+
/** One name the walk found that is not a directory. */
|
|
66
|
+
export interface TreeEntry {
|
|
67
|
+
fsPath: string;
|
|
68
|
+
/** `true` for a regular file; `false` for a link or anything else. */
|
|
69
|
+
regular: boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The page's `splitCase` followed by its `assemble` per pack, over the walk's
|
|
73
|
+
* tree. `read` opens a file; it is called for the files a pack owns and for
|
|
74
|
+
* nothing else.
|
|
75
|
+
*/
|
|
76
|
+
export declare function splitCase(tree: Map<string, TreeEntry>, read: (entry: TreeEntry, key: string) => Promise<Uint8Array>): Promise<SplitCase>;
|
|
77
|
+
/** `verify`'s reader, with the refusal named by the pack's path. */
|
|
78
|
+
export declare function loadBundle(pack: SplitPack): unknown;
|
|
79
|
+
/**
|
|
80
|
+
* A regular file's bytes. Opened without following a link and without
|
|
81
|
+
* blocking, then checked again, so a file swapped for a link or a pipe after
|
|
82
|
+
* the walk listed it is refused rather than followed or waited on.
|
|
83
|
+
*/
|
|
84
|
+
export declare function readFileEntry(entry: TreeEntry, key: string): Promise<Uint8Array>;
|
|
85
|
+
/**
|
|
86
|
+
* Every name under the directories named, keyed `<name>/<path inside>`. Each
|
|
87
|
+
* argument must be a directory, and not a link to one. A `.zip` is refused
|
|
88
|
+
* with the way through.
|
|
89
|
+
*/
|
|
90
|
+
export declare function treeFromArguments(paths: readonly string[]): Promise<Map<string, TreeEntry>>;
|
|
91
|
+
export declare const CASE_EXIT_CODES: Readonly<Record<string, number>>;
|
|
92
|
+
export declare const CASE_USAGE = "sanning-proof case -- one verdict over several evidence packs, offline\n\n sanning-proof case <folder>... [--gateways <url,url>] [--json]\n\n <folder> a pack folder (bundle.json plus logs/), or a folder that holds\n several packs, such as an unpacked decision file. case reads\n folders only: given a .zip, it exits 2 and says to unzip it\n first (for example, unzip file.zip -d case/) and pass the folder.\n Packs are found by the verify page's splitting rule: every\n bundle.json is a pack, a file belongs to the deepest pack whose\n bundle.json or logs/ it sits in, and every other file is listed\n as outside every pack and never opened. A .zip inside a folder is\n a file like any other. A symbolic link is never followed: it is\n listed as outside and not read, even under a pack's logs/.\n Several folders are read as one folder holding them, each under\n its own name. A folder nested more than 32 levels deep, more than\n 50000 files and folders in all, or a name that is not valid UTF-8\n exits 2.\n --gateways comma-separated gateway URLs. Each pack's witnesses are then\n re-fetched on chain, pack by pack, and reported beside the case\n reading. Without it the on-chain half is NOT checked, and the\n output says so.\n --json the full reading as JSON: the verdict, every exception with its\n rendered line, every record, every contribution, the packs, the\n files outside them, and the on-chain half.\n\nEach pack's bundle.json is read as verify reads it, so the two commands\nclassify one bundle the same way.\n\nWhat a case reading establishes: every pack is verified offline by this\nkernel; every record every pack supplied is read, in one pile, under the\nhand-off rules of envelope-spec section 4.1; a hand-off record with no\ncounterpart among the case's records is always named, and when a pack signed by\nthe key it names is in the case, the reading says that pack holds none, unless\nevery candidate it had was accounted for by another record (A20), which reads\nas no counterpart and names no pack.\n\nWhat it does not establish: that the case is complete (a party can decline to\nhand over a pack); that a party which presented no pack under the named key\nrecorded nothing; that two organizations are independent of each other; or\nthat any witness was re-fetched on chain, which the reading never does.\n\nExit codes: 0 verified \u00B7 1 failed \u00B7 2 malformed, usage, an input that cannot\nbe read as a case, or an unexpected error in this command \u00B7 3 incomplete or\npartial (undetermined, not failed) \u00B7 4 mixed (more than one outcome: read the\nexceptions). With --gateways, a witness that does not match on chain exits 1,\nand an on-chain half that could not be settled turns 0 into 3.";
|
|
93
|
+
/**
|
|
94
|
+
* The case verdict's code, moved only by an on-chain half that was asked for:
|
|
95
|
+
* a witness that does not match is a failure whatever the reading said, and an
|
|
96
|
+
* on-chain half that could not be settled is never a clean 0.
|
|
97
|
+
*/
|
|
98
|
+
export declare function caseExitCode(verdict: string, onChainVerdict: string): number;
|
|
99
|
+
/**
|
|
100
|
+
* `proof case <folder>... [--gateways <urls>] [--json]`. Every path out of the
|
|
101
|
+
* command is an exit code from the table: an exception nobody expected exits 2,
|
|
102
|
+
* never 1, because 1 would name the evidence failed over a defect in this
|
|
103
|
+
* command.
|
|
104
|
+
*/
|
|
105
|
+
export declare function runCase(argv: readonly string[], io: Cli): Promise<number>;
|
|
8
106
|
export {};
|