@mikeargento/bitgraph-audit 0.1.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 (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/dist/anchors.d.ts +31 -0
  4. package/dist/anchors.d.ts.map +1 -0
  5. package/dist/anchors.js +116 -0
  6. package/dist/anchors.js.map +1 -0
  7. package/dist/anomalies.d.ts +42 -0
  8. package/dist/anomalies.d.ts.map +1 -0
  9. package/dist/anomalies.js +642 -0
  10. package/dist/anomalies.js.map +1 -0
  11. package/dist/attestation.d.ts +20 -0
  12. package/dist/attestation.d.ts.map +1 -0
  13. package/dist/attestation.js +795 -0
  14. package/dist/attestation.js.map +1 -0
  15. package/dist/audit.d.ts +19 -0
  16. package/dist/audit.d.ts.map +1 -0
  17. package/dist/audit.js +116 -0
  18. package/dist/audit.js.map +1 -0
  19. package/dist/authority.d.ts +37 -0
  20. package/dist/authority.d.ts.map +1 -0
  21. package/dist/authority.js +126 -0
  22. package/dist/authority.js.map +1 -0
  23. package/dist/aws-nitro-root-ca.d.ts +24 -0
  24. package/dist/aws-nitro-root-ca.d.ts.map +1 -0
  25. package/dist/aws-nitro-root-ca.js +44 -0
  26. package/dist/aws-nitro-root-ca.js.map +1 -0
  27. package/dist/cli.d.ts +3 -0
  28. package/dist/cli.d.ts.map +1 -0
  29. package/dist/cli.js +257 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/contents-hash.d.ts +30 -0
  32. package/dist/contents-hash.d.ts.map +1 -0
  33. package/dist/contents-hash.js +73 -0
  34. package/dist/contents-hash.js.map +1 -0
  35. package/dist/export.d.ts +64 -0
  36. package/dist/export.d.ts.map +1 -0
  37. package/dist/export.js +192 -0
  38. package/dist/export.js.map +1 -0
  39. package/dist/index.d.ts +31 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +17 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/ingest.d.ts +36 -0
  44. package/dist/ingest.d.ts.map +1 -0
  45. package/dist/ingest.js +828 -0
  46. package/dist/ingest.js.map +1 -0
  47. package/dist/reconstruct.d.ts +40 -0
  48. package/dist/reconstruct.d.ts.map +1 -0
  49. package/dist/reconstruct.js +400 -0
  50. package/dist/reconstruct.js.map +1 -0
  51. package/dist/report-json.d.ts +3 -0
  52. package/dist/report-json.d.ts.map +1 -0
  53. package/dist/report-json.js +290 -0
  54. package/dist/report-json.js.map +1 -0
  55. package/dist/report-md.d.ts +3 -0
  56. package/dist/report-md.d.ts.map +1 -0
  57. package/dist/report-md.js +853 -0
  58. package/dist/report-md.js.map +1 -0
  59. package/dist/rlp.d.ts +37 -0
  60. package/dist/rlp.d.ts.map +1 -0
  61. package/dist/rlp.js +120 -0
  62. package/dist/rlp.js.map +1 -0
  63. package/dist/tar.d.ts +47 -0
  64. package/dist/tar.d.ts.map +1 -0
  65. package/dist/tar.js +483 -0
  66. package/dist/tar.js.map +1 -0
  67. package/dist/temporal.d.ts +55 -0
  68. package/dist/temporal.d.ts.map +1 -0
  69. package/dist/temporal.js +461 -0
  70. package/dist/temporal.js.map +1 -0
  71. package/dist/types.d.ts +1389 -0
  72. package/dist/types.d.ts.map +1 -0
  73. package/dist/types.js +3 -0
  74. package/dist/types.js.map +1 -0
  75. package/dist/validity.d.ts +21 -0
  76. package/dist/validity.d.ts.map +1 -0
  77. package/dist/validity.js +86 -0
  78. package/dist/validity.js.map +1 -0
  79. package/dist/verify-tiers.d.ts +11 -0
  80. package/dist/verify-tiers.d.ts.map +1 -0
  81. package/dist/verify-tiers.js +120 -0
  82. package/dist/verify-tiers.js.map +1 -0
  83. package/dist/witness.d.ts +25 -0
  84. package/dist/witness.d.ts.map +1 -0
  85. package/dist/witness.js +308 -0
  86. package/dist/witness.js.map +1 -0
  87. package/package.json +42 -0
  88. package/src/anchors.ts +168 -0
  89. package/src/anomalies.ts +773 -0
  90. package/src/attestation.ts +878 -0
  91. package/src/audit.ts +136 -0
  92. package/src/authority.ts +165 -0
  93. package/src/aws-nitro-root-ca.ts +47 -0
  94. package/src/cli.ts +284 -0
  95. package/src/contents-hash.ts +95 -0
  96. package/src/export.ts +278 -0
  97. package/src/index.ts +122 -0
  98. package/src/ingest.ts +967 -0
  99. package/src/reconstruct.ts +491 -0
  100. package/src/report-json.ts +331 -0
  101. package/src/report-md.ts +1067 -0
  102. package/src/rlp.ts +142 -0
  103. package/src/tar.ts +587 -0
  104. package/src/temporal.ts +629 -0
  105. package/src/types.ts +1528 -0
  106. package/src/validity.ts +90 -0
  107. package/src/verify-tiers.ts +137 -0
  108. package/src/witness.ts +407 -0
@@ -0,0 +1,1389 @@
1
+ /**
2
+ * bitgraph-audit types
3
+ *
4
+ * Public data structures for the BitGraph Audit Bundle consumer.
5
+ * This file intentionally contains no logic.
6
+ *
7
+ * Two dimensions are kept deliberately separate throughout:
8
+ *
9
+ * - Verification status: what the canonical verify package said about a
10
+ * proof object (and whether artifact bytes were available to say it).
11
+ * - Chain topology: where the proof sits in the causal record (counters,
12
+ * predecessor links, epochs, chains). Reconstruction and anomaly
13
+ * classification build on this in later stages.
14
+ *
15
+ * A verifier failure is never reinterpreted as a chain anomaly, and a chain
16
+ * observation never upgrades a proof's verification status.
17
+ */
18
+ /**
19
+ * Stable machine-readable codes for audit findings.
20
+ *
21
+ * This is an OPEN union: the listed literals are the codes emitted by the
22
+ * ingest and verification layers, and later stages (chain reconstruction,
23
+ * anchor analysis, attestation validation) extend it with additional codes
24
+ * (for example unexplained counter positions, counter collisions,
25
+ * predecessor reuse, chain breaks, authority divergence, epoch link
26
+ * anomalies). The `(string & {})` arm keeps the type open for those
27
+ * extensions while preserving literal completion for the known codes.
28
+ *
29
+ * Codes are contract: report consumers match on them and must never need
30
+ * to parse English to determine what happened.
31
+ */
32
+ export type AnomalyCode =
33
+ /** Proof-shaped file whose version is not exactly "bitgraph/1". Rejected at ingest. */
34
+ "unsupported-version"
35
+ /** A stored copy carries an embedded proofHash that does not match the computed canonical hash. */
36
+ | "proofhash-mismatch"
37
+ /** Byte-identical proof file observed at more than one path. */
38
+ | "exact-duplicate"
39
+ /** Same canonical proof identity observed in different byte encodings. */
40
+ | "semantic-duplicate"
41
+ /** Container entry whose path is absolute, escapes the bundle root, or contains NUL. Skipped entirely. */
42
+ | "unsafe-path"
43
+ /** Multiple tar entries normalized to the same path; the last entry wins. */
44
+ | "duplicate-path"
45
+ /** Root manifest.json failed to parse as a single JSON object. */
46
+ | "manifest-unparseable"
47
+ /** Root manifest.json parsed but its version field is missing or unrecognized. */
48
+ | "manifest-unrecognized-version"
49
+ /** A manifest field is present but fails the type rules of the bundle spec (section 7.1). */
50
+ | "manifest-field-invalid"
51
+ /** The manifest's contentsHashB64 does not match the computed deterministic contents hash. Advisory; never a proof failure. */
52
+ | "manifest-contents-hash-mismatch"
53
+ /** Counter positions inside a partition's observed range that are neither a commit counter nor a referenced slot counter. Each may be a proof absent from the bundle or a slot that was allocated but never committed (routine); the audit cannot distinguish these offline and never asserts authority failure. */
54
+ | "unexplained-counter-positions"
55
+ /** Two or more valid non-identical proofs claim the same commit counter in one partition. */
56
+ | "counter-collision"
57
+ /** Two or more valid non-identical proofs reference the same slot counter in one partition. */
58
+ | "slot-collision"
59
+ /** A commit counter value in one proof equals a different valid proof's slotCounter in the same partition: one causal position double-allocated across kinds. Only possible through enclave malfunction, replay, or compromise. */
60
+ | "cross-kind-position-reuse"
61
+ /** One prevB64 predecessor hash is claimed by two or more valid successors: a detectable fork. All branches are preserved. */
62
+ | "predecessor-reuse"
63
+ /** prevB64 references a predecessor absent from the bundle (no observed proof has that canonical hash). */
64
+ | "chain-break-missing"
65
+ /** prevB64 is not decodable as standard base64 of 32 bytes; it can never match a canonical proof hash. */
66
+ | "chain-break-malformed"
67
+ /** prevB64 resolves to an observed proof in a different partition (different signer, epoch, or chain). prevB64 never bridges partitions. */
68
+ | "chain-break-cross-partition"
69
+ /** More than one proof without prevB64 in a single partition. A single genesis without prevB64 is normal and never an anomaly. */
70
+ | "multiple-genesis"
71
+ /** commit.slotCounter is not strictly less than commit.counter. */
72
+ | "slot-order-violation"
73
+ /** epochLink references a prior epoch that is observed, but the referenced terminal proof is absent from the bundle. */
74
+ | "epochlink-terminal-missing"
75
+ /** epochLink references a prior epoch and terminal proof, neither of which is observed in the bundle. */
76
+ | "epochlink-dangling"
77
+ /** The same predecessor terminal is consumed by genesis proofs of two or more distinct successor epochs. */
78
+ | "epochlink-fork"
79
+ /** epochLink lineage edges form a cycle among epochs: the claimed ordering is self-contradictory. */
80
+ | "epochlink-cycle"
81
+ /** epochLink's prevProofHashB64 matches an observed proof, but the link's declared epoch, key, or counter disagrees with that proof. */
82
+ | "epochlink-mismatch"
83
+ /** Two or more distinct signer keys appear within a single epochId. */
84
+ | "mid-epoch-signer-change"
85
+ /** Two or more distinct declared measurements appear within a single epochId. */
86
+ | "mid-epoch-measurement-change"
87
+ /** Signed attribution identifies an Ethereum anchor, but the unsigned metadata.type is present and disagrees. The signed field governs. */
88
+ | "anchor-metadata-disagreement"
89
+ /** Unsigned metadata claims ethereum-anchor, but the signed attribution does not. Metadata alone never makes a proof an anchor. */
90
+ | "anchor-metadata-only-claim"
91
+ /** An anchor's signed attribution.title does not parse as an Etherscan block URL; the signed block number is treated as absent. */
92
+ | "anchor-title-unparseable"
93
+ /** Anchor witness file fails the field rules of the bundle spec (section 10.2). Confers nothing. */
94
+ | "witness-malformed"
95
+ /** Anchor witness headerRlpHex is not a single well-formed RLP list. Confers nothing. */
96
+ | "witness-rlp-invalid"
97
+ /** Witness header decoded and hashed correctly, but the RLP items at index 8 (number) or 11 (timestamp) are missing or not byte strings. */
98
+ | "witness-header-shape"
99
+ /** Locally recomputed block hash does not equal the anchor's signed attribution.message (spec 10.3 step 3). */
100
+ | "witness-hash-mismatch"
101
+ /** SHA-256 of the exact signed attribution.message string does not equal the anchor's artifact digest (spec 10.3 step 4). */
102
+ | "witness-digest-mismatch"
103
+ /** The header's number field disagrees with the witness's claimed blockNumber or the signed Etherscan URL (spec 10.3 step 5). */
104
+ | "witness-block-number-mismatch"
105
+ /** The witness's claimed blockHash does not equal the locally recomputed block hash (spec 10.3 step 5). */
106
+ | "witness-claimed-hash-mismatch"
107
+ /** The candidate anchor proof is not cryptographically valid; a witness cannot rescue an invalid proof (spec 10.3 preconditions). */
108
+ | "witness-anchor-invalid"
109
+ /** The witness's reconstructed (and claimed) block hash matches no observed anchor's signed attribution.message. */
110
+ | "witness-unmatched"
111
+ /** An attestation document is present but failed offline cryptographic validation (COSE, chain, root, or validity window). */
112
+ | "attestation-invalid"
113
+ /** A validated attestation document's PCR0 does not equal the proof's declared environment.measurement. */
114
+ | "attestation-measurement-mismatch"
115
+ /** A validated attestation document's user_data is not bound to this proof's canonical proof hash. */
116
+ | "attestation-user-data-mismatch" | (string & {});
117
+ /**
118
+ * A single machine-readable audit finding.
119
+ *
120
+ * Findings are advisory observations about the bundle and its files. They
121
+ * never change a proof's verification status; verification results live on
122
+ * the ObservedProof records themselves.
123
+ */
124
+ export interface AuditFinding {
125
+ code: AnomalyCode;
126
+ /** Bundle-root-relative path of the entry this finding is about, when applicable. */
127
+ path?: string;
128
+ /** Plain-language description. Consumers must key on `code`, not this text. */
129
+ message: string;
130
+ /** Extra machine-readable detail specific to the code. */
131
+ details?: Record<string, unknown>;
132
+ }
133
+ /**
134
+ * Which verification path ran for a proof.
135
+ *
136
+ * "full" verify() from @mikeargento/bitgraph-verify, with the
137
+ * original artifact bytes (content-addressed match found
138
+ * in the bundle). The artifact digest comparison ran.
139
+ * "integrity" verifyProofIntegrity(): every check verify() performs
140
+ * except the artifact digest comparison. No artifact bytes
141
+ * were available. Artifact binding was NOT checked.
142
+ */
143
+ export type VerificationTier = "full" | "integrity";
144
+ /**
145
+ * Outcome of the verification pass for a proof.
146
+ *
147
+ * "verified" Full tier only: verify() passed with the
148
+ * original artifact bytes.
149
+ * "failed" The canonical checks failed at either tier.
150
+ * The exact verifier reason is recorded.
151
+ * "artifact-unavailable" Integrity tier only: the bytes-free checks
152
+ * passed, but no artifact bytes were present in
153
+ * the bundle, so digest matching was not
154
+ * independently checked. Never reported as
155
+ * "verified".
156
+ */
157
+ export type VerificationStatus = "verified" | "failed" | "artifact-unavailable";
158
+ /** Result of cross-checking an embedded proofHash field against the computed canonical hash. */
159
+ export type EmbeddedProofHashStatus = "absent" | "match" | "mismatch";
160
+ /**
161
+ * Verification record attached to an ObservedProof by the verification
162
+ * pass (verifyObservedProofs). Absent until that pass has run.
163
+ */
164
+ export interface ProofVerification {
165
+ tier: VerificationTier;
166
+ status: VerificationStatus;
167
+ /** Exact failure reason from the canonical verifier. Present only when status is "failed". */
168
+ reason?: string;
169
+ /** Bundle-root-relative path of the artifact bytes used. Full tier only. */
170
+ artifactPath?: string;
171
+ }
172
+ /** One source file observed to carry a proof. */
173
+ export interface ProofSource {
174
+ /** Bundle-root-relative path. */
175
+ path: string;
176
+ /** Lowercase hex SHA-256 over the file's raw bytes (byte-level identity, distinct from the canonical proof hash). */
177
+ fileSha256Hex: string;
178
+ /** Raw byte length of the file. */
179
+ byteLength: number;
180
+ }
181
+ /**
182
+ * A unique observed proof, keyed by canonical identity.
183
+ *
184
+ * Identity is the canonical proof hash (base64 SHA-256 over the canonical
185
+ * signed body, computed by computeProofHash from
186
+ * @mikeargento/bitgraph-verify). Byte-identical copies and re-encodings of
187
+ * the same proof collapse into one ObservedProof with multiple sources.
188
+ *
189
+ * Chain metadata fields are extracted best-effort at ingest so that
190
+ * reconstruction never needs to re-walk raw files. Structurally invalid
191
+ * member candidates are still recorded here (the bundle spec requires it);
192
+ * their missing fields stay undefined and their precise failure reason is
193
+ * produced by the verification pass.
194
+ */
195
+ export interface ObservedProof {
196
+ /** Canonical identity: base64 SHA-256 over the canonical signed body. Always computed, never read from the file. */
197
+ proofHash: string;
198
+ /**
199
+ * The parsed proof object, retained for the verification pass.
200
+ * Report emitters serialize the compact metadata fields below, not this.
201
+ */
202
+ proof: import("@mikeargento/bitgraph-verify").BitGraphProof;
203
+ /** Every source file observed carrying this canonical identity, in observation order. */
204
+ sources: ProofSource[];
205
+ /** Schema version. Members are always exactly "bitgraph/1" (version policy). */
206
+ version: "bitgraph/1";
207
+ /** commit.counter when present (decimal string, epoch-local and chain-local). */
208
+ counter?: string;
209
+ /** commit.slotCounter when present: the counter position consumed by this proof's slot. */
210
+ slotCounter?: string;
211
+ /** commit.prevB64 when present: canonical hash of the predecessor proof in the same epoch and chain. */
212
+ prevB64?: string;
213
+ /** commit.epochId when present. */
214
+ epochId?: string;
215
+ /**
216
+ * Chain identifier from the signed commit body. The live enclave injects
217
+ * this as an undeclared field for non-default chains; a proof without it
218
+ * belongs to the enclave's default chain, normalized here to the literal
219
+ * string "global".
220
+ */
221
+ chainId: string;
222
+ /** signer.publicKeyB64, when structurally present. */
223
+ publicKeyB64?: string;
224
+ /** environment.measurement (declared, self-reported), when structurally present. */
225
+ measurement?: string;
226
+ /** environment.enforcement (declared, self-reported), when structurally present. */
227
+ enforcement?: string;
228
+ /** Whether a slotAllocation record is embedded. */
229
+ hasSlotAllocation: boolean;
230
+ /** Whether an environment.attestation document is present. Presence alone proves nothing. */
231
+ hasAttestation: boolean;
232
+ /** Whether an agency envelope is present. */
233
+ hasAgency: boolean;
234
+ /** Whether commit.epochLink is present (epoch lineage evidence). */
235
+ hasEpochLink: boolean;
236
+ /** Cross-check of any embedded proofHash field (stored-form proofs) against the computed canonical hash. */
237
+ embeddedProofHash: EmbeddedProofHashStatus;
238
+ /**
239
+ * True when the proof carries neither commit.counter nor commit.epochId:
240
+ * observed-but-unchained. Permitted by the schema, reported separately,
241
+ * not an anomaly.
242
+ */
243
+ chainless: boolean;
244
+ /** Filled by the verification pass (verifyObservedProofs). */
245
+ verification?: ProofVerification;
246
+ }
247
+ /**
248
+ * A proof-shaped file rejected under the version policy: the audit system
249
+ * supports exactly bitgraph/1. Rejected inputs are counted and listed but
250
+ * are excluded from verification, chain reconstruction, and anomaly
251
+ * analysis. They are not observed objects and not chain members.
252
+ */
253
+ export interface UnsupportedVersionRecord {
254
+ code: "unsupported-version";
255
+ /** Bundle-root-relative path. */
256
+ path: string;
257
+ /** The offending version string exactly as it appeared. */
258
+ version: string;
259
+ /** Lowercase hex SHA-256 over the file's raw bytes. */
260
+ fileSha256Hex: string;
261
+ }
262
+ /**
263
+ * A candidate artifact: any entry that is not a member proof, not an anchor
264
+ * witness, and not the root manifest. Indexed by content hash for
265
+ * content-addressed matching against proof digests. Filenames are never
266
+ * load-bearing.
267
+ */
268
+ export interface ArtifactRecord {
269
+ /** Lowercase hex SHA-256 over the entry's raw bytes. */
270
+ sha256Hex: string;
271
+ /** The same digest in standard base64, the form proofs carry in artifact.digestB64. */
272
+ sha256B64: string;
273
+ /** Raw byte length. */
274
+ byteLength: number;
275
+ /** Every path holding these exact bytes, in observation order. Byte-identical copies create no ambiguity. */
276
+ paths: string[];
277
+ /** Canonical hashes of every observed proof whose artifact digest equals this content hash. One artifact may satisfy many proofs. */
278
+ matchedProofHashes: string[];
279
+ }
280
+ /**
281
+ * An anchor witness file discovered by its version discriminator
282
+ * ("bitgraph-anchor-witness/1"). Ingest records it; the anchor analysis
283
+ * stage performs the mandatory verification procedure of the bundle spec
284
+ * (section 10.3). An unverified witness confers nothing.
285
+ */
286
+ export interface AnchorWitnessFile {
287
+ /** Bundle-root-relative path. */
288
+ path: string;
289
+ /** Lowercase hex SHA-256 over the file's raw bytes. */
290
+ fileSha256Hex: string;
291
+ /** The parsed witness object, unvalidated beyond the version discriminator. */
292
+ witness: Record<string, unknown>;
293
+ }
294
+ /**
295
+ * The optional root manifest, as declared by the producer. Every field is
296
+ * unsigned and advisory; the consumer cross-checks and reports, it never
297
+ * treats manifest fields as evidence about proofs, ordering, or time.
298
+ */
299
+ export interface BundleManifest {
300
+ version: string;
301
+ epochIds?: string[];
302
+ chainIds?: string[];
303
+ proofCount?: number;
304
+ counterRanges?: Array<{
305
+ epochId: string;
306
+ chainId: string;
307
+ min: string;
308
+ max: string;
309
+ }>;
310
+ generatedAt?: string;
311
+ contentsHashB64?: string;
312
+ artifactsIncluded?: boolean;
313
+ openEpochs?: Array<{
314
+ epochId: string;
315
+ counterAtSnapshot: string;
316
+ }>;
317
+ /** Unknown fields are tolerated and preserved here untyped. */
318
+ [key: string]: unknown;
319
+ }
320
+ /** What the consumer found at the reserved root path manifest.json. */
321
+ export interface ManifestReport {
322
+ /** Always "manifest.json"; the only load-bearing filename in the format. */
323
+ path: string;
324
+ /** Whether the file parsed as a single JSON object. */
325
+ parsed: boolean;
326
+ /** The raw version value when parsed, whatever it was. */
327
+ version?: string;
328
+ /** Whether version was exactly "bitgraph-bundle/1". When false the manifest is reported but not interpreted. */
329
+ recognized: boolean;
330
+ /** The manifest object, present when parsed and recognized. */
331
+ manifest?: BundleManifest;
332
+ /** Field validation problems per the bundle spec section 7.1. Advisory. */
333
+ problems: string[];
334
+ /** Contents hash comparison, present when the manifest declared contentsHashB64. */
335
+ contentsHash?: {
336
+ declaredB64: string;
337
+ computedB64: string;
338
+ match: boolean;
339
+ };
340
+ }
341
+ /** Accepted container forms per the bundle spec section 4. */
342
+ export type ContainerKind = "directory" | "tar" | "tar-gz";
343
+ export interface IngestCounts {
344
+ /** Unique observed proofs by canonical identity. */
345
+ observed: number;
346
+ /** Proof-carrying files, including duplicate copies. */
347
+ proofFiles: number;
348
+ /** Byte-identical duplicate proof files beyond the first copy. */
349
+ exactDuplicates: number;
350
+ /** Re-encoded duplicate proof files (same canonical identity, different bytes) beyond the first copy. */
351
+ semanticDuplicates: number;
352
+ /** Proof-shaped files rejected under the version policy. */
353
+ unsupportedVersion: number;
354
+ /** Candidate artifact files (unique by content hash). */
355
+ artifacts: number;
356
+ /** Anchor witness files. */
357
+ witnesses: number;
358
+ /** Container entries skipped for unsafe paths. */
359
+ skippedUnsafePaths: number;
360
+ }
361
+ /**
362
+ * Resource caps applied to untrusted .tar / .tar.gz ingest so a crafted
363
+ * archive (a decompression bomb, a header declaring an absurd size, or a
364
+ * flood of tiny entries) aborts with a clear error instead of exhausting
365
+ * memory. Directory bundles are already materialized on disk and are not
366
+ * capped here. All caps are well above legitimate bundle sizes; the
367
+ * defaults are DEFAULT_INGEST_LIMITS.
368
+ */
369
+ export interface IngestLimits {
370
+ /**
371
+ * Ceiling on the total number of decompressed bytes read from a tar or
372
+ * tar.gz container across the whole archive. Exceeding it aborts ingest.
373
+ */
374
+ maxTotalBytes: number;
375
+ /**
376
+ * Ceiling on the number of tar entries (headers) processed. Exceeding it
377
+ * aborts ingest, bounding a flood of zero-length entries.
378
+ */
379
+ maxEntryCount: number;
380
+ /**
381
+ * Ceiling on the size of a single tar metadata entry that is buffered
382
+ * whole (PAX extended headers and GNU long-name entries). A header
383
+ * declaring a larger size aborts ingest before any allocation.
384
+ */
385
+ maxMetadataEntryBytes: number;
386
+ }
387
+ /**
388
+ * Everything the ingest pass learned about a bundle.
389
+ *
390
+ * Memory shape: proofs, witnesses, and per-entry metadata are held in
391
+ * memory (O(number of entries) plus the parsed proof objects themselves);
392
+ * artifact bytes are never retained, only their streaming hashes. The
393
+ * verification pass re-reads matched artifact bytes one artifact at a time.
394
+ */
395
+ export interface IngestResult {
396
+ /** The path the bundle was opened from, exactly as given. */
397
+ bundlePath: string;
398
+ container: ContainerKind;
399
+ /**
400
+ * Present when the tar container had a single common top-level directory
401
+ * that was stripped per the bundle spec section 4.1 (the usual result of
402
+ * `tar -czf bundle.tgz mybundle/`). All reported paths are relative to
403
+ * the stripped root.
404
+ */
405
+ strippedRootPrefix?: string;
406
+ /** Total regular-file entries scanned, including skipped and duplicate-path entries. */
407
+ entriesScanned: number;
408
+ /** Unique observed proofs in first-observation order. */
409
+ proofs: ObservedProof[];
410
+ /** Proof-shaped files rejected under the version policy. */
411
+ unsupportedVersions: UnsupportedVersionRecord[];
412
+ /** Candidate artifacts, unique by content hash, in first-observation order. */
413
+ artifacts: ArtifactRecord[];
414
+ /** Anchor witness files in observation order. */
415
+ witnesses: AnchorWitnessFile[];
416
+ /** Present when a root manifest.json entry existed. */
417
+ manifest?: ManifestReport;
418
+ /**
419
+ * The deterministic contents hash computed over every final entry except
420
+ * the root manifest.json, per the bundle spec section 8. Computed whether
421
+ * or not a manifest declared one.
422
+ */
423
+ computedContentsHashB64: string;
424
+ /** Machine-readable findings, in detection order. */
425
+ findings: AuditFinding[];
426
+ counts: IngestCounts;
427
+ }
428
+ /** Options for the verification pass. */
429
+ export interface VerifyObservedOptions {
430
+ /**
431
+ * Optional policy constraints passed through unchanged to the canonical
432
+ * verifier (both verify() and verifyProofIntegrity()).
433
+ */
434
+ trustAnchors?: import("@mikeargento/bitgraph-verify").VerificationPolicy;
435
+ }
436
+ /** Aggregate outcome of the verification pass. */
437
+ export interface VerificationSummary {
438
+ /** Unique proofs processed. */
439
+ total: number;
440
+ /** Full tier, canonical verify() passed with artifact bytes. */
441
+ verified: number;
442
+ /** Canonical checks failed at either tier. */
443
+ failed: number;
444
+ /** Integrity checks passed but no artifact bytes were available; binding not independently checked. */
445
+ artifactUnavailable: number;
446
+ /** Proofs with neither counter nor epochId (observed-but-unchained). Informational; orthogonal to status. */
447
+ chainless: number;
448
+ }
449
+ /** One matched artifact's bytes, yielded by streamMatchedArtifacts. */
450
+ export interface MatchedArtifactBytes {
451
+ /** Lowercase hex SHA-256 of the bytes. */
452
+ sha256Hex: string;
453
+ /** Bundle-root-relative path the bytes were read from. */
454
+ path: string;
455
+ bytes: Uint8Array;
456
+ }
457
+ /**
458
+ * A partition of the observed record: one signer lineage on one chain in
459
+ * one epoch, per G6. Multiple signer lineages are never merged. Proofs
460
+ * that carry a counter but no epochId partition with epochId absent.
461
+ */
462
+ export interface PartitionKey {
463
+ /** signer.publicKeyB64. */
464
+ publicKeyB64: string;
465
+ /** commit.epochId; absent when the proof carries a counter without an epochId. */
466
+ epochId?: string;
467
+ /** Chain identifier, normalized to "global" when the signed body omits it. */
468
+ chainId: string;
469
+ }
470
+ /**
471
+ * A connected chain component reconstructed from prevB64 hash links.
472
+ *
473
+ * Hash links are the primary reconstruction evidence: an edge exists from
474
+ * proof P to proof S exactly when S's commit.prevB64 equals P's canonical
475
+ * proof hash and both are members of the same partition. Counters are
476
+ * ordering and anomaly evidence, never the reconstruction mechanism.
477
+ */
478
+ export interface ChainComponent {
479
+ /**
480
+ * Member canonical hashes in link order: a deterministic traversal from
481
+ * the component's genesis and broken-link entry points, following
482
+ * successor hash links (branches ordered by counter, then hash).
483
+ */
484
+ memberProofHashes: string[];
485
+ /** Members with no prevB64 field at all: epoch genesis candidates (normal per G1). */
486
+ genesisProofHashes: string[];
487
+ /** Members whose prevB64 did not resolve to any member of this partition (chain-break entry points). */
488
+ brokenLinkProofHashes: string[];
489
+ /** Members with no observed successor in this partition. */
490
+ terminalProofHashes: string[];
491
+ /** Whether any member carries a parseable commit counter or slot counter. */
492
+ hasCounterEvidence: boolean;
493
+ /** Min and max over all parseable commit and slot counter positions of the members. */
494
+ positionRange?: {
495
+ min: string;
496
+ max: string;
497
+ };
498
+ }
499
+ /** One partition's reconstructed chain structure. */
500
+ export interface ChainPartition {
501
+ key: PartitionKey;
502
+ /** All member canonical hashes, sorted by counter evidence then hash. */
503
+ memberProofHashes: string[];
504
+ /** Connected components, sorted by lowest counter position then first member hash. */
505
+ components: ChainComponent[];
506
+ }
507
+ /** The six epochLink fields exactly as declared in the signed commit body. */
508
+ export interface EpochLinkFields {
509
+ prevEpochId: string;
510
+ prevPublicKeyB64: string;
511
+ prevCounter: string;
512
+ prevProofHashB64: string;
513
+ toEpochId: string;
514
+ toPublicKeyB64: string;
515
+ }
516
+ /**
517
+ * Analysis of one observed epochLink: cross-epoch lineage evidence per G1.
518
+ *
519
+ * "matched" prevProofHashB64 equals the canonical hash of an
520
+ * observed proof.
521
+ * "terminal-missing" the prior epoch is observed, but the referenced
522
+ * terminal proof is absent from the bundle.
523
+ * "dangling" neither the referenced proof nor the prior epoch
524
+ * is observed.
525
+ */
526
+ export interface EpochLineageEdge {
527
+ /** Predecessor epoch, from the link. */
528
+ fromEpochId: string;
529
+ /** Successor epoch, from the link. */
530
+ toEpochId: string;
531
+ /** Canonical hash of the proof carrying the epochLink. */
532
+ viaProofHash: string;
533
+ /** The declared link fields (part of the signed body of the via proof). */
534
+ link: EpochLinkFields;
535
+ resolution: "matched" | "terminal-missing" | "dangling";
536
+ /** Canonical hash of the observed predecessor proof. Matched only. */
537
+ predecessorProofHash?: string;
538
+ /** Whether the observed predecessor's epochId, signer key, and counter agree with the link's declared fields. Matched only. */
539
+ metadataConsistent?: boolean;
540
+ /** Plain-language descriptions of each disagreement. Matched and inconsistent only. */
541
+ inconsistencies?: string[];
542
+ /** Whether the observed predecessor has no observed successor within its own partition. Matched and partitioned predecessors only. */
543
+ referencedProofIsTerminal?: boolean;
544
+ /**
545
+ * Intrinsic cryptographic validity of the via proof: run verification
546
+ * status when it passed, otherwise an isolated bytes-free recheck that
547
+ * is immune to run-order effects (the epoch link single-successor check
548
+ * depends on verification order) and to supplied trust policy.
549
+ */
550
+ viaProofValid: boolean;
551
+ /** Intrinsic validity of the observed predecessor. Matched only. */
552
+ predecessorValid?: boolean;
553
+ /**
554
+ * True when this edge is hard cross-epoch ordering evidence: matched,
555
+ * metadata-consistent, and both proofs intrinsically valid. Only hard
556
+ * edges derive epoch ordering.
557
+ */
558
+ hardEdge: boolean;
559
+ }
560
+ /**
561
+ * Typed extension point for anchor-derived one-sided temporal bounds.
562
+ * Reconstruction always leaves EpochRecord.anchorBounds undefined; the
563
+ * anchor analysis stage (Phase 4c) populates it from verified anchor
564
+ * evidence. Overlapping or absent bounds mean concurrent-or-unordered,
565
+ * never divergence.
566
+ */
567
+ export interface EpochAnchorBound {
568
+ /**
569
+ * "not-after": proofs of this epoch existed before the referenced
570
+ * anchor evidence. "not-before": they came after it. Always one-sided.
571
+ */
572
+ kind: "not-after" | "not-before";
573
+ /** Canonical hash of the anchor proof providing the bound. */
574
+ anchorProofHash: string;
575
+ /** Ethereum block number parsed from the signed anchor title URL (decimal string). */
576
+ blockNumber?: string;
577
+ /** Ethereum block hash from the signed attribution message. */
578
+ blockHash?: string;
579
+ /**
580
+ * Unix seconds from an offline-verified anchor witness header. Present
581
+ * only when witness material allowed local reconstruction of the block
582
+ * hash. Never sourced from unsigned metadata.
583
+ */
584
+ witnessTimestamp?: number;
585
+ /**
586
+ * Which of the epoch's proofs this bound covers. Anchors sit inside
587
+ * epochs, so a bound never covers a whole epoch: a "not-before" bound
588
+ * covers members causally after its anchor, a "not-after" bound covers
589
+ * members causally before its anchor. The uncovered remainder has no
590
+ * bound from this anchor.
591
+ */
592
+ coverage?: "members-before-anchor" | "members-after-anchor";
593
+ /** How many of the epoch's partitioned proofs this bound covers. */
594
+ coveredProofCount?: number;
595
+ /** Total partitioned proofs observed for the epoch. */
596
+ totalProofCount?: number;
597
+ /**
598
+ * What grounds the bound. "block-hash-unpredictability" (not-before):
599
+ * the block hash was unpredictable before the block's timestamp and the
600
+ * covered proofs embed it through the chain. "causal-precedence"
601
+ * (not-after): the covered proofs existed before the anchor commit,
602
+ * which is proven to be no earlier than the block timestamp; reading the
603
+ * timestamp as a wall-clock ceiling additionally assumes the anchor
604
+ * consumed a recently published block.
605
+ */
606
+ basis?: "block-hash-unpredictability" | "causal-precedence";
607
+ /**
608
+ * Evidence class of the representative bound this epoch bound rests on
609
+ * (the minimum-timestamp bound for not-before, the maximum-timestamp
610
+ * bound for not-after): "chain-link" (a verified prevB64 hash-link path)
611
+ * or "counter-order" (commit-counter ordering only, which relies on the
612
+ * authority's counter discipline). Undefined until the temporal stage
613
+ * (Phase 4c) populates it.
614
+ */
615
+ evidence?: BoundEvidence;
616
+ /** True when evidence is "counter-order": the epoch bound rests on weaker evidence. */
617
+ weaker?: boolean;
618
+ /** Plain-language statement of exactly what this bound claims. */
619
+ claim?: string;
620
+ }
621
+ /** One observed epoch, aggregated across chains and partitions. */
622
+ export interface EpochRecord {
623
+ epochId: string;
624
+ /** Chains on which this epoch produced observed proofs, sorted. */
625
+ chainIds: string[];
626
+ /** Distinct signer keys observed under this epochId, sorted. One is normal; more is an authority anomaly. */
627
+ publicKeysB64: string[];
628
+ /** Unique observed proofs carrying this epochId. */
629
+ proofCount: number;
630
+ /**
631
+ * "linked" when the epoch participates in at least one hard epochLink
632
+ * lineage edge. "observed-but-unordered" otherwise: the bundle contains
633
+ * no verified evidence ordering this epoch against any other. Epochs
634
+ * without lineage or anchor evidence are concurrent-or-unordered
635
+ * relative to each other, which is never divergence.
636
+ */
637
+ ordering: "linked" | "observed-but-unordered";
638
+ /** Epochs verified to come directly before this one (hard edges only). */
639
+ linkedPredecessorEpochIds: string[];
640
+ /** Epochs verified to come directly after this one (hard edges only). */
641
+ linkedSuccessorEpochIds: string[];
642
+ /** Anchor-derived one-sided bounds. Undefined until the anchor analysis stage (Phase 4c) populates it. */
643
+ anchorBounds?: EpochAnchorBound[];
644
+ }
645
+ /** Cross-epoch relationships per G1: independent chains, ordered only by verified evidence. */
646
+ export interface EpochRelationshipResult {
647
+ /** Observed epochs, sorted by epochId. */
648
+ epochs: EpochRecord[];
649
+ /** Every observed epochLink, analyzed. Includes non-hard edges with their failure modes. */
650
+ edges: EpochLineageEdge[];
651
+ /**
652
+ * Transitive ordering derived from hard lineage edges only. Pairs whose
653
+ * evidence is contradictory (reachable in both directions, which only a
654
+ * lineage cycle produces) are removed rather than asserted either way.
655
+ * Epoch pairs absent from this list are concurrent-or-unordered.
656
+ */
657
+ orderedPairs: Array<{
658
+ beforeEpochId: string;
659
+ afterEpochId: string;
660
+ }>;
661
+ }
662
+ /** Everything the reconstruction pass derived from an ingested bundle. */
663
+ export interface ReconstructionResult {
664
+ /** Partitions per (signer key, epochId, chainId), deterministically sorted. */
665
+ partitions: ChainPartition[];
666
+ /** Canonical hashes of chainless proofs (neither counter nor epochId): observed-but-unchained, outside all partitions, not an anomaly. */
667
+ unchainedProofHashes: string[];
668
+ /** Canonical hashes of proofs carrying chain fields but no signer key: they cannot join a signer lineage. */
669
+ unpartitionedProofHashes: string[];
670
+ epochRelationships: EpochRelationshipResult;
671
+ }
672
+ /**
673
+ * A chain-level anomaly: a structural observation about the reconstructed
674
+ * record. Chain topology and verification status are separate dimensions:
675
+ * an anomaly never changes a proof's verification status, and a verifier
676
+ * failure is never reinterpreted as a chain anomaly.
677
+ */
678
+ export interface ChainAnomaly {
679
+ code: AnomalyCode;
680
+ /** The partition this anomaly is scoped to, when partition-scoped. */
681
+ partition?: PartitionKey;
682
+ /** Canonical hashes of the observed proofs involved. Empty when the anomaly is about absent proofs. */
683
+ proofHashes: string[];
684
+ /** Plain language. Consumers key on `code`, never on this text. */
685
+ message: string;
686
+ /** Machine-readable detail specific to the code. All values are JSON-safe strings, arrays, or objects. */
687
+ details?: Record<string, unknown>;
688
+ }
689
+ /**
690
+ * Detail payload of an "unexplained-counter-positions" anomaly (G2).
691
+ * A position is explained when it is some observed proof's commit counter
692
+ * or is referenced by some observed proof's slotCounter. Every proof
693
+ * consumes two positions; slot positions never produce stored proofs.
694
+ */
695
+ export interface UnexplainedPositionsDetail {
696
+ /** Total unexplained positions, decimal string (BigInt-safe). */
697
+ count: string;
698
+ /** Contiguous unexplained ranges, ascending, inclusive. */
699
+ ranges: Array<{
700
+ start: string;
701
+ end: string;
702
+ }>;
703
+ /** Flat position list, ascending, capped; see truncated. */
704
+ positions: string[];
705
+ /** True when count exceeded the flat-list cap and positions is incomplete. Ranges are always complete. */
706
+ truncated: boolean;
707
+ }
708
+ /** The conflicts that produce divergence records. */
709
+ export type DivergenceKind = "counter-collision" | "slot-collision" | "cross-kind-position-reuse" | "predecessor-reuse" | "multiple-genesis" | "epochlink-fork";
710
+ /** One competing (or context) proof in a divergence, with everything a reader needs to adjudicate. */
711
+ export interface DivergenceParty {
712
+ proofHash: string;
713
+ /** Every bundle path this proof was observed at. */
714
+ sourcePaths: string[];
715
+ verificationTier?: VerificationTier;
716
+ verificationStatus?: VerificationStatus;
717
+ verificationReason?: string;
718
+ counter?: string;
719
+ slotCounter?: string;
720
+ prevB64?: string;
721
+ publicKeyB64?: string;
722
+ epochId?: string;
723
+ chainId: string;
724
+ measurement?: string;
725
+ }
726
+ /**
727
+ * A conflict between valid proof objects. All parties are preserved; the
728
+ * audit never selects a winner by file order, counter height, verification
729
+ * order, or any other heuristic. The reader adjudicates.
730
+ *
731
+ * Cryptographically invalid proofs related to the same conflict appear in
732
+ * invalidContext, never among the competing parties. Intrinsic validity is
733
+ * assessed independently of run-order effects (the epoch link
734
+ * single-successor check fails whichever consumer verifies second) and of
735
+ * supplied trust policy; the recorded run verification status of every
736
+ * party is shown unmodified.
737
+ */
738
+ export interface DivergenceRecord {
739
+ kind: DivergenceKind;
740
+ /** The partition the conflict occurred in, when partition-scoped. */
741
+ partition?: PartitionKey;
742
+ /** The contested resource, e.g. { counter: "18442" } or { prevB64: "..." }. */
743
+ contested: Record<string, string>;
744
+ /** Valid competing branches. Always two or more. */
745
+ parties: DivergenceParty[];
746
+ /** Cryptographically invalid observed proofs involved in the same conflict. Context only, never competing branches. */
747
+ invalidContext: DivergenceParty[];
748
+ /** Plain-language structural explanation of the conflict. */
749
+ explanation: string;
750
+ }
751
+ /** Output of the anomaly classification pass. */
752
+ export interface AnomalyReport {
753
+ /** Chain anomalies in deterministic order (partition order, then detection order within a partition). */
754
+ anomalies: ChainAnomaly[];
755
+ /** Divergence records for every conflict between valid proofs. */
756
+ divergences: DivergenceRecord[];
757
+ }
758
+ /**
759
+ * Attested-measurement evidence for an authority group.
760
+ *
761
+ * TYPED EXTENSION POINT: authority analysis never populates this. The
762
+ * attestation validation stage (Phase 4c) fills it after cryptographically
763
+ * validating attestation documents offline. Declared measurement
764
+ * (environment.measurement, self-reported) and attested measurement (from
765
+ * a validated attestation document) are never conflated: the fields live
766
+ * apart and the declared value never appears here.
767
+ */
768
+ export interface AttestedMeasurementEvidence {
769
+ status: "validated" | "validation-failed" | "unsupported";
770
+ /** PCR0 measurement extracted from a cryptographically validated attestation document. Present when the group's validated documents attest exactly one value. */
771
+ attestedMeasurement?: string;
772
+ /** Whether the attested measurement equals the declared environment.measurement. */
773
+ matchesDeclared?: boolean;
774
+ /** Member proofs whose attestation documents validated. */
775
+ validatedProofCount?: number;
776
+ /** Member proofs whose attestation documents failed validation. */
777
+ failedProofCount?: number;
778
+ /** All distinct PCR0 values attested by the group's validated documents, when more than one. */
779
+ attestedMeasurements?: string[];
780
+ }
781
+ /** One authority group: proofs sharing declared measurement, signer key, epoch, chain, and attestation presence. */
782
+ export interface AuthorityGroup {
783
+ /** Declared environment.measurement (self-reported; presence proves nothing). */
784
+ measurement?: string;
785
+ publicKeyB64?: string;
786
+ epochId?: string;
787
+ chainId: string;
788
+ /** Whether an attestation document is present on these proofs. Presence alone is not validation. */
789
+ attestationPresent: boolean;
790
+ /** Canonical hashes of member proofs, in observation order. */
791
+ proofHashes: string[];
792
+ /** Filled by the attestation validation stage (Phase 4c). Always undefined here. */
793
+ attested?: AttestedMeasurementEvidence;
794
+ }
795
+ /** A signer key observed under more than one epochId: normal epoch-transition evidence, never an anomaly. */
796
+ export interface SignerEpochSpan {
797
+ publicKeyB64: string;
798
+ /** Sorted epochIds this key signed under. */
799
+ epochIds: string[];
800
+ }
801
+ /** Output of the authority analysis pass. */
802
+ export interface AuthorityAnalysis {
803
+ /** Authority groups, deterministically sorted. */
804
+ groups: AuthorityGroup[];
805
+ /** mid-epoch-signer-change and mid-epoch-measurement-change anomalies. */
806
+ anomalies: ChainAnomaly[];
807
+ /** Same-signer-across-epochs observations (normal transition evidence). */
808
+ sharedSignersAcrossEpochs: SignerEpochSpan[];
809
+ }
810
+ /**
811
+ * How the unsigned metadata relates to the signed anchor identification.
812
+ * The signed attribution.name is the discriminator; metadata.type is
813
+ * corroboration only and is never trusted alone.
814
+ */
815
+ export type AnchorMetadataCorroboration = "agrees" | "disagrees" | "absent";
816
+ /**
817
+ * One identified Ethereum anchor proof (G5): an ordinary chain member
818
+ * whose SIGNED attribution.name is exactly "Ethereum Anchor".
819
+ *
820
+ * Everything here comes from the signed body or the run verification
821
+ * record. The block timestamp is deliberately absent: it exists only in
822
+ * unsigned metadata (never trusted) and in verified witness headers
823
+ * (see AnchorWitnessOutcome). No wall-clock time is ever derived from a
824
+ * block number.
825
+ */
826
+ export interface AnchorRecord {
827
+ /** Canonical proof hash of the anchor proof. */
828
+ proofHash: string;
829
+ epochId?: string;
830
+ chainId: string;
831
+ /** Commit counter (the anchor's causal position in its partition). */
832
+ counter?: string;
833
+ slotCounter?: string;
834
+ /** The Ethereum block hash string from the SIGNED attribution.message, exactly as signed. */
835
+ blockHash?: string;
836
+ /**
837
+ * Block number parsed from the SIGNED Etherscan URL in
838
+ * attribution.title (decimal string). Absent when the title is missing
839
+ * or does not parse; unparseable titles are reported, never guessed.
840
+ */
841
+ blockNumber?: string;
842
+ metadataCorroboration: AnchorMetadataCorroboration;
843
+ /** Run verification record, copied from the observed proof. */
844
+ verificationTier?: VerificationTier;
845
+ verificationStatus?: VerificationStatus;
846
+ verificationReason?: string;
847
+ }
848
+ /** Output of the anchor identification pass. */
849
+ export interface AnchorIdentification {
850
+ /** Identified anchors, in observation order. */
851
+ anchors: AnchorRecord[];
852
+ /**
853
+ * Proofs whose unsigned metadata claims ethereum-anchor while the
854
+ * signed attribution does not. Never treated as anchors.
855
+ */
856
+ metadataOnlyProofHashes: string[];
857
+ /** anchor-metadata-disagreement, anchor-metadata-only-claim, anchor-title-unparseable findings. */
858
+ findings: AuditFinding[];
859
+ }
860
+ /**
861
+ * Outcome of the mandatory witness verification procedure (bundle spec
862
+ * 10.3) for one witness file against one candidate anchor. A witness
863
+ * confers evidence only when verified is true; any failure means it
864
+ * confers nothing and the anchor proof's own standing is unchanged.
865
+ */
866
+ export interface AnchorWitnessOutcome {
867
+ /** Bundle-root-relative path of the witness file. */
868
+ witnessPath: string;
869
+ /** Canonical hash of the candidate anchor proof. Absent when the witness matched no anchor or failed before matching. */
870
+ anchorProofHash?: string;
871
+ verified: boolean;
872
+ /** Stable failure code. Present exactly when verified is false. */
873
+ reason?: AnomalyCode;
874
+ /** Plain-language failure detail. Consumers key on reason. */
875
+ detail?: string;
876
+ /** Locally recomputed Keccak-256 block hash (0x + 64 lowercase hex), when the header decoded. */
877
+ computedBlockHash?: string;
878
+ /** Block number from the header's RLP index 8 (decimal string), when read. */
879
+ blockNumber?: string;
880
+ /**
881
+ * Unix seconds from the header's RLP index 11. PRESENT ONLY when every
882
+ * verification step passed: this is the external wall-clock evidence.
883
+ */
884
+ timestamp?: number;
885
+ }
886
+ /** Output of the witness verification pass. */
887
+ export interface AnchorWitnessAnalysis {
888
+ /** One outcome per (witness, candidate anchor) pair, plus one per unusable or unmatched witness. */
889
+ outcomes: AnchorWitnessOutcome[];
890
+ /** witness-* findings for every failure, in detection order. */
891
+ findings: AuditFinding[];
892
+ }
893
+ /**
894
+ * What relates a bounded proof to the anchor supplying the bound.
895
+ *
896
+ * "chain-link" a verified prevB64 hash-link path connects the proof
897
+ * and the anchor within the partition. The relation is
898
+ * independently checkable from the objects themselves.
899
+ * "counter-order" only the commit counters order them. This relies on
900
+ * the authority's per-chain counter discipline rather
901
+ * than verifiable hash links, and is marked weaker.
902
+ */
903
+ export type BoundEvidence = "chain-link" | "counter-order";
904
+ /**
905
+ * One one-sided temporal bound on a segment, derived from a verified
906
+ * anchor witness. Never an interval by itself, and never a statement of
907
+ * any individual proof's exact creation time.
908
+ *
909
+ * Bound semantics, stated precisely:
910
+ *
911
+ * "not-before": the covered proofs were COMMITTED no earlier than the
912
+ * block timestamp. Grounded in block-hash unpredictability: the hash
913
+ * did not exist before the block, the anchor commit consumed it, and
914
+ * the covered proofs come after the anchor. This additionally assumes
915
+ * the anchored header is a genuine, publicly published Ethereum block:
916
+ * the offline audit checks the header's structure and hash binding, not
917
+ * proof-of-work, consensus, or chain membership, so it cannot confirm
918
+ * the block is real. Sound along chain-link evidence, subject to that
919
+ * assumption.
920
+ *
921
+ * "not-after": the covered proofs existed before the anchor commit
922
+ * that consumed a block published at the timestamp. The block
923
+ * timestamp proves the anchor commit came AT OR AFTER it, not how
924
+ * promptly, so reading the timestamp as a wall-clock ceiling
925
+ * additionally assumes the anchor consumed a recently published block
926
+ * (the deployed anchor service commits the latest block on a short
927
+ * interval, but that is service behavior, not proof). The causal
928
+ * precedence itself is sound along chain-link evidence.
929
+ */
930
+ export interface SegmentBound {
931
+ kind: "not-before" | "not-after";
932
+ anchorProofHash: string;
933
+ /** Block number confirmed by the verified witness header (decimal string). */
934
+ blockNumber?: string;
935
+ /** Locally recomputed block hash (0x + 64 lowercase hex). */
936
+ blockHash: string;
937
+ /** Unix seconds from the verified witness header. */
938
+ timestamp: number;
939
+ evidence: BoundEvidence;
940
+ /** True for counter-order evidence: weaker, as documented on BoundEvidence. */
941
+ weaker: boolean;
942
+ basis: "block-hash-unpredictability" | "causal-precedence";
943
+ /** Plain-language statement of exactly what this bound claims and assumes. */
944
+ claim: string;
945
+ }
946
+ export type TemporalSegmentStatus =
947
+ /** Verified anchor evidence exists on both sides. Still two one-sided bounds, reported together. */
948
+ "bracketed" | "lower-bounded" | "upper-bounded"
949
+ /** No verified anchor evidence relates to these proofs. Their causal order stands; no wall-clock claim is made. */
950
+ | "ordered-but-unanchored";
951
+ /**
952
+ * A group of proofs in one partition sharing the same verified-anchor
953
+ * bound set. Bounds attach to segments, never to individual proofs.
954
+ */
955
+ export interface TemporalSegment {
956
+ partition: PartitionKey;
957
+ /** Member canonical hashes, ordered by counter evidence then hash. */
958
+ memberProofHashes: string[];
959
+ /** Min and max over the members' parseable commit and slot counter positions. */
960
+ positionRange?: {
961
+ min: string;
962
+ max: string;
963
+ };
964
+ status: TemporalSegmentStatus;
965
+ /**
966
+ * Tightest not-before bounds, tightest first. At most two entries: the
967
+ * tightest overall, plus the tightest chain-link bound when the overall
968
+ * tightest rests only on counter ordering.
969
+ */
970
+ lowerBounds: SegmentBound[];
971
+ /** Tightest not-after bounds, same structure as lowerBounds. */
972
+ upperBounds: SegmentBound[];
973
+ }
974
+ /**
975
+ * Anchor-derived cross-epoch ordering evidence. Always about the COVERED
976
+ * portions of the two epochs (see EpochAnchorBound.coverage), and always
977
+ * dependent on the not-after freshness assumption documented on
978
+ * SegmentBound. Epoch pairs without such evidence are
979
+ * concurrent-or-unordered, which is never divergence.
980
+ */
981
+ export interface AnchorOrderedPair {
982
+ beforeEpochId: string;
983
+ afterEpochId: string;
984
+ /** The before-epoch's covering not-after bound. */
985
+ upperAnchorProofHash: string;
986
+ upperBoundTimestamp: number;
987
+ /** The after-epoch's covering not-before bound. */
988
+ lowerAnchorProofHash: string;
989
+ lowerBoundTimestamp: number;
990
+ basis: "anchor-bounds";
991
+ /** Always true: the upper side of the comparison rests on the anchor-freshness assumption. */
992
+ assumptionDependent: true;
993
+ /** Evidence class of the before-epoch's not-after (upper) bound. */
994
+ upperEvidence: BoundEvidence;
995
+ /** Evidence class of the after-epoch's not-before (lower) bound. */
996
+ lowerEvidence: BoundEvidence;
997
+ /** True when either side rests on "counter-order" evidence: the cross-epoch ordering is weaker. */
998
+ weaker: boolean;
999
+ beforeCoveredProofCount: number;
1000
+ beforeTotalProofCount: number;
1001
+ afterCoveredProofCount: number;
1002
+ afterTotalProofCount: number;
1003
+ note: string;
1004
+ }
1005
+ /** Output of the temporal bounds pass. */
1006
+ export interface TemporalAnalysis {
1007
+ /** Per-partition segments with their bounds, deterministically ordered. */
1008
+ segments: TemporalSegment[];
1009
+ /** Anchor-derived cross-epoch ordering evidence (assumption-dependent; see AnchorOrderedPair). */
1010
+ anchorOrderedPairs: AnchorOrderedPair[];
1011
+ /** Anchors with at least one verified witness, sorted by proof hash. */
1012
+ verifiedAnchorProofHashes: string[];
1013
+ /** Identified anchors with no verified witness: they still establish causal order, but confer no wall-clock evidence. Sorted. */
1014
+ unverifiedAnchorProofHashes: string[];
1015
+ }
1016
+ /** One named validation check, mirroring the shape of the website validator's check list. */
1017
+ export interface AttestationCheck {
1018
+ name: string;
1019
+ pass: boolean;
1020
+ detail: string;
1021
+ }
1022
+ /** Options for the low-level attestation document validator. */
1023
+ export interface NitroValidationOptions {
1024
+ /** Declared measurement to compare PCR0 against. Compared only after the document validates. */
1025
+ expectedPcr0?: string;
1026
+ /** Canonical proof hash the attestation's user_data must be bound to. Compared only after the document validates. */
1027
+ expectedUserDataB64?: string;
1028
+ /**
1029
+ * DER bytes of the trust anchor the certificate chain must terminate
1030
+ * at. Defaults to the bundled AWS Nitro Enclaves Root CA G1. Supplying
1031
+ * other trust material is for tests and non-AWS deployments; the audit
1032
+ * pipeline default is always the bundled AWS root.
1033
+ */
1034
+ trustedRootCaDer?: Uint8Array;
1035
+ }
1036
+ /** Result of the low-level offline attestation document validation. */
1037
+ export interface NitroValidationResult {
1038
+ /**
1039
+ * True when the DOCUMENT checks all passed: COSE decode, payload parse,
1040
+ * leaf presence, ECDSA P-384 signature over the Sig_structure,
1041
+ * certificate chain walk, trust-root anchoring, and certificate
1042
+ * validity windows evaluated at the document's own timestamp. Says
1043
+ * nothing about PCR0 or user_data binding; those are separate facts.
1044
+ */
1045
+ documentValid: boolean;
1046
+ /** Every check performed, in order. Later checks are absent when an earlier one failed. */
1047
+ checks: AttestationCheck[];
1048
+ /** Detail of the first failed document check. */
1049
+ failure?: string;
1050
+ /** PCR0 from the attestation payload (lowercase hex). Zero-valued PCRs are treated as absent. */
1051
+ pcr0?: string;
1052
+ /** All non-zero PCRs (lowercase hex by index). */
1053
+ pcrs: Record<number, string>;
1054
+ moduleId?: string;
1055
+ /** The attestation document's own timestamp, milliseconds since epoch. */
1056
+ timestamp?: number;
1057
+ certChainLength?: number;
1058
+ /** Base64 of the document's user_data bytes, when present. */
1059
+ userDataB64?: string;
1060
+ /** PCR0 equals expectedPcr0. Present only when expectedPcr0 was given AND the document validated. */
1061
+ pcr0Matches?: boolean;
1062
+ /** user_data is bound to expectedUserDataB64. Present only when expectedUserDataB64 was given AND the document validated. */
1063
+ userDataMatches?: boolean;
1064
+ }
1065
+ /**
1066
+ * Per-proof attestation facts. The five facts the report must never
1067
+ * conflate are tracked separately: declared measurement present,
1068
+ * attestation document present, document cryptographically validated,
1069
+ * attested PCR0 matches declared measurement, user_data bound to the
1070
+ * signed body. pcr0MatchesDeclared and userDataBoundToProof are set only
1071
+ * when the document validated; values parsed from an unvalidated
1072
+ * document prove nothing and are never compared.
1073
+ */
1074
+ export interface ProofAttestationRecord {
1075
+ proofHash: string;
1076
+ declaredMeasurementPresent: boolean;
1077
+ declaredMeasurement?: string;
1078
+ documentPresent: boolean;
1079
+ /** environment.attestation.format, when declared. */
1080
+ attestationFormat?: string;
1081
+ documentValidated: boolean;
1082
+ /** Precise failure reason when documentValidated is false and a document was present. */
1083
+ validationFailure?: string;
1084
+ /** The full check list from the validator. Empty when no document was present. */
1085
+ checks: AttestationCheck[];
1086
+ attestedPcr0?: string;
1087
+ pcrs?: Record<number, string>;
1088
+ moduleId?: string;
1089
+ timestamp?: number;
1090
+ certChainLength?: number;
1091
+ userDataB64?: string;
1092
+ /** Present only when the document validated and a declared measurement exists. */
1093
+ pcr0MatchesDeclared?: boolean;
1094
+ /** Present only when the document validated. */
1095
+ userDataBoundToProof?: boolean;
1096
+ }
1097
+ /** Output of the attestation validation pass. */
1098
+ export interface AttestationAnalysis {
1099
+ /** One record per observed proof, in observation order. */
1100
+ records: ProofAttestationRecord[];
1101
+ /** attestation-* findings, in detection order. */
1102
+ findings: AuditFinding[];
1103
+ counts: {
1104
+ proofsWithDeclaredMeasurement: number;
1105
+ proofsWithDocument: number;
1106
+ documentsValidated: number;
1107
+ documentsFailed: number;
1108
+ pcr0Matches: number;
1109
+ pcr0Mismatches: number;
1110
+ userDataBound: number;
1111
+ userDataUnbound: number;
1112
+ };
1113
+ }
1114
+ /** Options for the full audit pipeline (runAudit). */
1115
+ export interface AuditOptions {
1116
+ /**
1117
+ * Optional trust policy passed unchanged to the canonical verifier at
1118
+ * both verification tiers. Policy rejections surface as verification
1119
+ * failures with the verifier's exact reason.
1120
+ */
1121
+ trustAnchors?: import("@mikeargento/bitgraph-verify").VerificationPolicy;
1122
+ /**
1123
+ * DER bytes of the attestation trust root. Defaults to the bundled AWS
1124
+ * Nitro Enclaves Root CA G1; supplying other material is for tests and
1125
+ * explicitly non-AWS deployments.
1126
+ */
1127
+ trustedRootCaDer?: Uint8Array;
1128
+ }
1129
+ /**
1130
+ * Run metadata for one audit execution. This is the ONLY nondeterministic
1131
+ * data the pipeline produces: startedAt is a wall-clock read, and nothing
1132
+ * else in an AuditResult or either report depends on the clock. Two runs
1133
+ * over the same bundle differ in this block alone.
1134
+ */
1135
+ export interface AuditRunMetadata {
1136
+ /** Tool version, read from the audit package's package.json. */
1137
+ toolVersion: string;
1138
+ /** ISO 8601 wall-clock start time of the run. */
1139
+ startedAt: string;
1140
+ /** The bundle path exactly as given to runAudit. */
1141
+ bundlePath: string;
1142
+ container: ContainerKind;
1143
+ }
1144
+ /** Everything one full audit run produced, in pipeline order. */
1145
+ export interface AuditResult {
1146
+ runMetadata: AuditRunMetadata;
1147
+ ingest: IngestResult;
1148
+ verification: VerificationSummary;
1149
+ reconstruction: ReconstructionResult;
1150
+ anomalies: AnomalyReport;
1151
+ authorities: AuthorityAnalysis;
1152
+ anchors: AnchorIdentification;
1153
+ witnesses: AnchorWitnessAnalysis;
1154
+ temporal: TemporalAnalysis;
1155
+ attestations: AttestationAnalysis;
1156
+ }
1157
+ /**
1158
+ * CLI exit semantics as bit flags.
1159
+ *
1160
+ * bit 1 (value 1): verification failures. Set when any proof's canonical
1161
+ * checks failed at either tier, or any proof-shaped input was rejected
1162
+ * as an unsupported version. artifact-unavailable is NOT a failure: a
1163
+ * proof without artifact bytes passes or fails on its bytes-free checks
1164
+ * alone, unless a supplied trust policy makes those checks fail (for
1165
+ * example requireSlot), in which case its status is "failed" and it
1166
+ * counts here. Attestation validation results never set this bit on
1167
+ * their own; they affect it only when a supplied policy made
1168
+ * verification itself fail.
1169
+ *
1170
+ * bit 2 (value 2): structural anomalies. Set when chain anomaly
1171
+ * classification or authority analysis produced any anomaly, any
1172
+ * divergence record between valid proofs exists, or any supplied anchor
1173
+ * witness failed its offline verification (a witness-* code: RLP or
1174
+ * header malformation, block-hash mismatch, digest-binding mismatch,
1175
+ * block-number mismatch, an invalid candidate anchor, or an unmatched
1176
+ * witness). Benign findings are reported but never set exit bits: ingest
1177
+ * advisories (duplicate copies, manifest advisories, unsafe paths,
1178
+ * embedded proofHash mismatches) and informational anchor findings
1179
+ * (anchor-metadata-disagreement, anchor-metadata-only-claim,
1180
+ * anchor-title-unparseable, all of which the signed body overrides).
1181
+ * Attestation validation results never set this bit on their own.
1182
+ */
1183
+ export interface ExitFlags {
1184
+ verificationFailures: boolean;
1185
+ /**
1186
+ * Bit 2: chain or authority anomalies, divergences between valid proofs,
1187
+ * or anchor witness verification failures. Informational anchor findings
1188
+ * and attestation results never set it.
1189
+ */
1190
+ chainAnomaliesOrDivergences: boolean;
1191
+ /** 0 clean, 1 verification failures, 2 structural anomalies (chain/authority/divergence/witness), 3 both. */
1192
+ code: number;
1193
+ }
1194
+ /** Which pipeline stage produced a reported anomaly. */
1195
+ export type AnomalyStage = "ingest" | "chain" | "authority" | "anchor" | "witness" | "attestation";
1196
+ /**
1197
+ * One entry of the report's unified anomaly list. Every classification is
1198
+ * a stable code; message text is supplementary and consumers must never
1199
+ * parse it.
1200
+ */
1201
+ export interface ReportAnomaly {
1202
+ stage: AnomalyStage;
1203
+ code: AnomalyCode;
1204
+ message: string;
1205
+ /** Bundle-root-relative path, for file-scoped findings. */
1206
+ path?: string;
1207
+ /** Partition scope, for partition-scoped chain anomalies. */
1208
+ partition?: PartitionKey;
1209
+ /** Canonical hashes of the observed proofs involved. */
1210
+ proofHashes?: string[];
1211
+ /** Machine-readable detail specific to the code. */
1212
+ details?: Record<string, unknown>;
1213
+ }
1214
+ /** Per-proof record in the JSON report. Compact fields only; never the raw proof object. */
1215
+ export interface ReportProofRecord {
1216
+ proofHash: string;
1217
+ sources: ProofSource[];
1218
+ verificationTier?: VerificationTier;
1219
+ verificationStatus?: VerificationStatus;
1220
+ verificationReason?: string;
1221
+ /** Bundle path of the artifact bytes used. Full tier only. */
1222
+ artifactPath?: string;
1223
+ embeddedProofHash: EmbeddedProofHashStatus;
1224
+ counter?: string;
1225
+ slotCounter?: string;
1226
+ prevB64?: string;
1227
+ epochId?: string;
1228
+ chainId: string;
1229
+ publicKeyB64?: string;
1230
+ measurement?: string;
1231
+ enforcement?: string;
1232
+ chainless: boolean;
1233
+ hasSlotAllocation: boolean;
1234
+ hasAttestation: boolean;
1235
+ hasAgency: boolean;
1236
+ hasEpochLink: boolean;
1237
+ }
1238
+ /** One partition in the JSON report, with its reconstructed components and an intactness verdict. */
1239
+ export interface ReportPartition {
1240
+ key: PartitionKey;
1241
+ memberProofHashes: string[];
1242
+ components: ChainComponent[];
1243
+ /**
1244
+ * True when the partition's observed record is one connected component
1245
+ * and no chain anomaly or divergence is scoped to this partition.
1246
+ */
1247
+ intact: boolean;
1248
+ }
1249
+ /** An epoch pair with no ordering evidence in either direction: concurrent-or-unordered, never divergence. */
1250
+ export interface UnorderedEpochPair {
1251
+ epochIdA: string;
1252
+ epochIdB: string;
1253
+ }
1254
+ /** Epoch relationship section of the JSON report. */
1255
+ export interface ReportEpochRelationships {
1256
+ /** Observed epochs, sorted by epochId, with anchorBounds populated by the temporal stage where evidence exists. */
1257
+ epochs: EpochRecord[];
1258
+ /** Every observed epochLink, analyzed. */
1259
+ lineageEdges: EpochLineageEdge[];
1260
+ /** Transitive ordering from hard lineage edges only. */
1261
+ orderedPairs: Array<{
1262
+ beforeEpochId: string;
1263
+ afterEpochId: string;
1264
+ }>;
1265
+ /** Anchor-derived, assumption-dependent ordering of covered epoch portions. */
1266
+ anchorOrderedPairs: AnchorOrderedPair[];
1267
+ /** Distinct epoch pairs with no ordering evidence from either source. */
1268
+ unorderedPairs: UnorderedEpochPair[];
1269
+ }
1270
+ /** Deterministic input summary of the JSON report. */
1271
+ export interface ReportInputSummary {
1272
+ container: ContainerKind;
1273
+ entriesScanned: number;
1274
+ strippedRootPrefix?: string;
1275
+ computedContentsHashB64: string;
1276
+ manifest?: ManifestReport;
1277
+ counts: {
1278
+ observed: number;
1279
+ proofFiles: number;
1280
+ exactDuplicates: number;
1281
+ semanticDuplicates: number;
1282
+ unsupportedVersion: number;
1283
+ verified: number;
1284
+ failed: number;
1285
+ artifactUnavailable: number;
1286
+ chainless: number;
1287
+ artifacts: number;
1288
+ witnesses: number;
1289
+ skippedUnsafePaths: number;
1290
+ };
1291
+ }
1292
+ /** Summary statistics of the JSON report. */
1293
+ export interface ReportSummary {
1294
+ proofsObserved: number;
1295
+ fullyVerified: number;
1296
+ failed: number;
1297
+ artifactUnavailable: number;
1298
+ unsupportedVersion: number;
1299
+ chainless: number;
1300
+ exactDuplicates: number;
1301
+ semanticDuplicates: number;
1302
+ partitions: number;
1303
+ partitionsIntact: number;
1304
+ /** True when every partition is intact and no chain or authority anomaly or divergence exists. */
1305
+ chainIntact: boolean;
1306
+ epochsObserved: number;
1307
+ /** Anomaly counts keyed by stable code, keys sorted. */
1308
+ anomalyCountsByCode: Record<string, number>;
1309
+ divergenceCount: number;
1310
+ authorityGroupCount: number;
1311
+ distinctSignerCount: number;
1312
+ distinctDeclaredMeasurementCount: number;
1313
+ /** The five separately tracked attestation facts (G9), as proof counts. */
1314
+ attestation: {
1315
+ declaredMeasurementPresent: number;
1316
+ documentsPresent: number;
1317
+ documentsValidated: number;
1318
+ pcr0MatchesDeclared: number;
1319
+ userDataBound: number;
1320
+ };
1321
+ temporal: {
1322
+ anchorsIdentified: number;
1323
+ anchorsWithVerifiedWitness: number;
1324
+ segments: number;
1325
+ segmentsBracketed: number;
1326
+ segmentsLowerBounded: number;
1327
+ segmentsUpperBounded: number;
1328
+ segmentsUnanchored: number;
1329
+ };
1330
+ exit: ExitFlags;
1331
+ }
1332
+ /**
1333
+ * The audit-report.json object, schema "bitgraph-audit-report/1".
1334
+ *
1335
+ * Deterministic given the same bundle except for the runMetadata block,
1336
+ * which is explicitly identified as the only nondeterministic section.
1337
+ * Object keys are built in fixed order; arrays are sorted by stable keys
1338
+ * (partition key, counter, canonical hash) or carry the deterministic
1339
+ * order of the producing stage. Machine consumers key on stable codes and
1340
+ * fields, never on message prose.
1341
+ */
1342
+ export interface AuditJsonReport {
1343
+ reportSchemaVersion: "bitgraph-audit-report/1";
1344
+ toolVersion: string;
1345
+ runMetadata: {
1346
+ /** Always true: this block is the only nondeterministic section of the report. */
1347
+ nondeterministic: true;
1348
+ note: string;
1349
+ toolVersion: string;
1350
+ startedAt: string;
1351
+ bundlePath: string;
1352
+ container: ContainerKind;
1353
+ };
1354
+ input: ReportInputSummary;
1355
+ /** Sorted by canonical proof hash. */
1356
+ proofs: ReportProofRecord[];
1357
+ /** Sorted by path. */
1358
+ unsupportedVersions: UnsupportedVersionRecord[];
1359
+ /** In the reconstruction pass's deterministic partition order. */
1360
+ partitions: ReportPartition[];
1361
+ unchainedProofHashes: string[];
1362
+ unpartitionedProofHashes: string[];
1363
+ epochRelationships: ReportEpochRelationships;
1364
+ /** Unified anomaly list: stage order (ingest, chain, authority, anchor, witness, attestation), stage-internal detection order. */
1365
+ anomalies: ReportAnomaly[];
1366
+ divergences: DivergenceRecord[];
1367
+ authorities: {
1368
+ groups: AuthorityGroup[];
1369
+ sharedSignersAcrossEpochs: SignerEpochSpan[];
1370
+ };
1371
+ attestations: {
1372
+ records: ProofAttestationRecord[];
1373
+ counts: AttestationAnalysis["counts"];
1374
+ };
1375
+ anchors: {
1376
+ records: AnchorRecord[];
1377
+ metadataOnlyProofHashes: string[];
1378
+ };
1379
+ witnesses: {
1380
+ outcomes: AnchorWitnessOutcome[];
1381
+ };
1382
+ temporal: {
1383
+ segments: TemporalSegment[];
1384
+ verifiedAnchorProofHashes: string[];
1385
+ unverifiedAnchorProofHashes: string[];
1386
+ };
1387
+ summary: ReportSummary;
1388
+ }
1389
+ //# sourceMappingURL=types.d.ts.map