@colophon-claims/verify 0.0.0 → 0.2.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 (96) hide show
  1. package/README.md +39 -0
  2. package/dist/admission/application.d.ts +18 -0
  3. package/dist/admission/application.js +53 -0
  4. package/dist/admission/contracts.d.ts +506 -0
  5. package/dist/admission/contracts.js +459 -0
  6. package/dist/admission/index.d.ts +5 -0
  7. package/dist/admission/index.js +5 -0
  8. package/dist/admission/intake.d.ts +75 -0
  9. package/dist/admission/intake.js +70 -0
  10. package/dist/admission/result-evaluation.d.ts +18 -0
  11. package/dist/admission/result-evaluation.js +71 -0
  12. package/dist/admission/screening-sample.d.ts +56 -0
  13. package/dist/admission/screening-sample.js +126 -0
  14. package/dist/admission/verification.d.ts +102 -0
  15. package/dist/admission/verification.js +844 -0
  16. package/dist/anchor/check.d.ts +95 -0
  17. package/dist/anchor/check.js +146 -0
  18. package/dist/anchor/ports.d.ts +6 -0
  19. package/dist/anchor/ports.js +354 -0
  20. package/dist/assets.d.ts +33 -0
  21. package/dist/assets.js +885 -0
  22. package/dist/bin.d.ts +2 -0
  23. package/dist/bin.js +20 -0
  24. package/dist/cli.d.ts +14 -0
  25. package/dist/cli.js +205 -0
  26. package/dist/comparison.d.ts +64 -0
  27. package/dist/comparison.js +235 -0
  28. package/dist/index.d.ts +23 -0
  29. package/dist/index.js +23 -0
  30. package/dist/manifest.d.ts +54 -0
  31. package/dist/manifest.js +240 -0
  32. package/dist/materialize.d.ts +4 -0
  33. package/dist/materialize.js +13 -0
  34. package/dist/profile/admission-receipts.d.ts +11 -0
  35. package/dist/profile/admission-receipts.js +24 -0
  36. package/dist/profile/anchor-claims.d.ts +157 -0
  37. package/dist/profile/anchor-claims.js +330 -0
  38. package/dist/profile/artifacts.d.ts +261 -0
  39. package/dist/profile/artifacts.js +139 -0
  40. package/dist/profile/assembly-ports.d.ts +19 -0
  41. package/dist/profile/assembly-ports.js +42 -0
  42. package/dist/profile/binary-judge-manifest.d.ts +121 -0
  43. package/dist/profile/binary-judge-manifest.js +106 -0
  44. package/dist/profile/binary-qualification.d.ts +9 -0
  45. package/dist/profile/binary-qualification.js +58 -0
  46. package/dist/profile/branding-assets.d.ts +5 -0
  47. package/dist/profile/branding-assets.js +5 -0
  48. package/dist/profile/branding.d.ts +24 -0
  49. package/dist/profile/branding.js +14 -0
  50. package/dist/profile/claim-consistency.d.ts +26 -0
  51. package/dist/profile/claim-consistency.js +91 -0
  52. package/dist/profile/claim.d.ts +327 -0
  53. package/dist/profile/claim.js +742 -0
  54. package/dist/profile/errors.d.ts +49 -0
  55. package/dist/profile/errors.js +78 -0
  56. package/dist/profile/inspect-assurance.d.ts +16 -0
  57. package/dist/profile/inspect-assurance.js +30 -0
  58. package/dist/profile/inspect-disclosure.d.ts +28 -0
  59. package/dist/profile/inspect-disclosure.js +54 -0
  60. package/dist/profile/inspect-manifest.d.ts +706 -0
  61. package/dist/profile/inspect-manifest.js +397 -0
  62. package/dist/profile/isolation.d.ts +21 -0
  63. package/dist/profile/isolation.js +31 -0
  64. package/dist/profile/pinning-evidence.d.ts +41 -0
  65. package/dist/profile/pinning-evidence.js +43 -0
  66. package/dist/profile/ports.d.ts +3 -0
  67. package/dist/profile/ports.js +11 -0
  68. package/dist/profile/preview-log.d.ts +4 -0
  69. package/dist/profile/preview-log.js +3 -0
  70. package/dist/profile/run-results.d.ts +17 -0
  71. package/dist/profile/run-results.js +23 -0
  72. package/dist/profile/signing.d.ts +10 -0
  73. package/dist/profile/signing.js +46 -0
  74. package/dist/profile/trust.d.ts +13 -0
  75. package/dist/profile/trust.js +35 -0
  76. package/dist/profile/venue.d.ts +2 -0
  77. package/dist/profile/venue.js +2 -0
  78. package/dist/profile/verdict.d.ts +28 -0
  79. package/dist/profile/verdict.js +51 -0
  80. package/dist/reader-instructions.d.ts +41 -0
  81. package/dist/reader-instructions.js +52 -0
  82. package/dist/schema.d.ts +380 -0
  83. package/dist/schema.js +498 -0
  84. package/dist/verify.d.ts +72 -0
  85. package/dist/verify.js +1573 -0
  86. package/dist/version.d.ts +2 -0
  87. package/dist/version.js +2 -0
  88. package/package.json +63 -3
  89. package/schemas/assembly-row.schema.json +194 -0
  90. package/schemas/bundle-manifest.schema.json +23 -0
  91. package/schemas/claim-package.schema.json +279 -0
  92. package/schemas/dsse-envelope.schema.json +25 -0
  93. package/schemas/evidence-catalog.schema.json +43 -0
  94. package/schemas/public-trust.schema.json +44 -0
  95. package/schemas/verdict-catalog.schema.json +27 -0
  96. package/scripts/external-verify.py +238 -0
@@ -0,0 +1,240 @@
1
+ import { createHash } from "node:crypto";
2
+ import { closeSync, constants, fstatSync, lstatSync, openSync, readFileSync, readdirSync, realpathSync, } from "node:fs";
3
+ import { isAbsolute, join, posix, relative, resolve, sep } from "node:path";
4
+ import { z } from "zod";
5
+ import { EvidenceNativeBundleManifestV5Schema } from "@jinn-network/benchmarking-protocol";
6
+ import { canonicalJsonBytes } from "@jinn-network/trust-core";
7
+ import { refuse } from "./profile/errors.js";
8
+ export const BUNDLE_FORMAT = "benchmark-product-public-bundle/2";
9
+ export const BUNDLE_V4_FORMAT = "benchmark-product-public-bundle/4";
10
+ export const BUNDLE_V5_FORMAT = "benchmark-product-public-bundle/5";
11
+ /** The anchored closure (anchor-evidence design §7.4). Additive: v2, v4, and v5 keep their check
12
+ * lists, byte shapes, and values; the `anchors/` member and the `integrity-anchors` check exist
13
+ * only here. */
14
+ export const BUNDLE_V6_FORMAT = "benchmark-product-public-bundle/6";
15
+ export const SUPPORTED_BUNDLE_FORMATS = [
16
+ BUNDLE_FORMAT,
17
+ BUNDLE_V4_FORMAT,
18
+ BUNDLE_V5_FORMAT,
19
+ BUNDLE_V6_FORMAT,
20
+ ];
21
+ export const BUNDLE_MANIFEST_FILENAME = "bundle.json";
22
+ const SHA256_HEX = /^[a-f0-9]{64}$/;
23
+ export const BundleManifestFileSchema = z.object({
24
+ path: z.string().min(1),
25
+ sha256: z.string().regex(SHA256_HEX),
26
+ bytes: z.number().int().nonnegative(),
27
+ });
28
+ const LegacyBundleManifestSchema = z.object({
29
+ format: z.union([
30
+ z.literal(BUNDLE_FORMAT),
31
+ z.literal(BUNDLE_V4_FORMAT),
32
+ z.literal(BUNDLE_V6_FORMAT),
33
+ ]),
34
+ files: z.array(BundleManifestFileSchema).min(1),
35
+ });
36
+ export const BundleManifestSchema = z.union([
37
+ LegacyBundleManifestSchema,
38
+ EvidenceNativeBundleManifestV5Schema,
39
+ ]);
40
+ function sha256(bytes) {
41
+ return createHash("sha256").update(bytes).digest("hex");
42
+ }
43
+ function equalBytes(left, right) {
44
+ return left.length === right.length && left.every((byte, index) => byte === right[index]);
45
+ }
46
+ function validateRelativePath(path) {
47
+ if (path === ""
48
+ || path === "."
49
+ || path === BUNDLE_MANIFEST_FILENAME
50
+ || isAbsolute(path)
51
+ || path.includes("\\")
52
+ || path.split("/").some((part) => part === "" || part === "." || part === "..")
53
+ || posix.normalize(path) !== path) {
54
+ refuse("record-integrity", "bundle.manifest.path", `unsafe or reserved bundle path "${path}"`);
55
+ }
56
+ }
57
+ function isInside(root, candidate) {
58
+ const rel = relative(root, candidate);
59
+ return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel));
60
+ }
61
+ function readRegularFile(bundleDir, path, allowManifest = false) {
62
+ if (allowManifest && path === BUNDLE_MANIFEST_FILENAME) {
63
+ // bundle.json is reserved from the file list but is itself authenticated by the same reader.
64
+ }
65
+ else {
66
+ validateRelativePath(path);
67
+ }
68
+ const absolute = join(bundleDir, ...path.split("/"));
69
+ let stat;
70
+ try {
71
+ stat = lstatSync(absolute);
72
+ }
73
+ catch {
74
+ refuse("record-integrity", path, `manifest entry "${path}" is missing`);
75
+ }
76
+ if (stat.isSymbolicLink()) {
77
+ refuse("record-integrity", path, `manifest entry "${path}" is a symbolic link`);
78
+ }
79
+ if (!stat.isFile()) {
80
+ refuse("record-integrity", path, `manifest entry "${path}" is not a regular file`);
81
+ }
82
+ if (stat.nlink !== 1) {
83
+ refuse("record-integrity", path, `manifest entry "${path}" must not be hard-linked`);
84
+ }
85
+ const root = realpathSync(bundleDir);
86
+ let resolved;
87
+ try {
88
+ resolved = realpathSync(absolute);
89
+ }
90
+ catch {
91
+ refuse("record-integrity", path, `manifest entry "${path}" cannot be resolved inside the bundle`);
92
+ }
93
+ if (!isInside(root, resolved)) {
94
+ refuse("record-integrity", path, `manifest entry "${path}" resolves outside the bundle`);
95
+ }
96
+ let fd;
97
+ try {
98
+ fd = openSync(absolute, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
99
+ }
100
+ catch {
101
+ refuse("record-integrity", path, `manifest entry "${path}" could not be opened without following links`);
102
+ }
103
+ try {
104
+ const opened = fstatSync(fd);
105
+ if (!opened.isFile() || opened.nlink !== 1 || opened.dev !== stat.dev || opened.ino !== stat.ino) {
106
+ refuse("record-integrity", path, `manifest entry "${path}" changed identity while it was opened`);
107
+ }
108
+ const bytes = new Uint8Array(readFileSync(fd));
109
+ const after = fstatSync(fd);
110
+ if (after.dev !== opened.dev || after.ino !== opened.ino || after.size !== opened.size) {
111
+ refuse("record-integrity", path, `manifest entry "${path}" changed while it was authenticated`);
112
+ }
113
+ return bytes;
114
+ }
115
+ finally {
116
+ closeSync(fd);
117
+ }
118
+ }
119
+ function walkTree(root, directory = root) {
120
+ const files = [];
121
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
122
+ const absolute = join(directory, entry.name);
123
+ const path = relative(root, absolute).split(sep).join("/");
124
+ const stat = lstatSync(absolute);
125
+ if (stat.isSymbolicLink()) {
126
+ refuse("record-integrity", path, `bundle contains symbolic link "${path}"`);
127
+ }
128
+ if (stat.isDirectory()) {
129
+ const resolvedRoot = realpathSync(root);
130
+ const resolvedDirectory = realpathSync(absolute);
131
+ if (!isInside(resolvedRoot, resolvedDirectory)) {
132
+ refuse("record-integrity", path, `bundle directory "${path}" resolves outside the bundle`);
133
+ }
134
+ files.push(...walkTree(root, absolute));
135
+ }
136
+ else if (stat.isFile()) {
137
+ files.push(path);
138
+ }
139
+ else {
140
+ refuse("record-integrity", path, `bundle contains non-regular entry "${path}"`);
141
+ }
142
+ }
143
+ return files;
144
+ }
145
+ export function buildBundleManifest(bundleDir, filePaths, options = {}) {
146
+ const seen = new Set();
147
+ const files = [...filePaths]
148
+ .map((path) => {
149
+ validateRelativePath(path);
150
+ if (seen.has(path)) {
151
+ refuse("record-integrity", "bundle.manifest.files", `duplicate bundle path "${path}"`);
152
+ }
153
+ seen.add(path);
154
+ const bytes = readRegularFile(bundleDir, path);
155
+ return { path, sha256: sha256(bytes), bytes: bytes.length };
156
+ })
157
+ .sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
158
+ const manifest = BundleManifestSchema.parse({ format: options.format ?? BUNDLE_FORMAT, files });
159
+ const bytes = canonicalJsonBytes(manifest);
160
+ return { manifest, bytes, identity: sha256(bytes) };
161
+ }
162
+ export function verifyBundleManifest(bundleDir) {
163
+ const snapshot = verifyBundleSnapshot(bundleDir);
164
+ return { manifest: snapshot.manifest, bytes: snapshot.bytes, identity: snapshot.identity };
165
+ }
166
+ /** Authenticates the complete tree once and returns the exact immutable byte snapshot used by
167
+ * every semantic verifier. No consumer may reopen a pathname after this boundary. */
168
+ export function verifyBundleSnapshot(bundleDir, deps = {}) {
169
+ const absoluteRoot = resolve(bundleDir);
170
+ let rootStat;
171
+ try {
172
+ rootStat = lstatSync(absoluteRoot);
173
+ }
174
+ catch (cause) {
175
+ if (cause instanceof Error && "code" in cause && cause.code === "ENOENT") {
176
+ refuse("record-integrity", "bundle", "bundle directory is missing");
177
+ }
178
+ throw cause;
179
+ }
180
+ if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
181
+ refuse("record-integrity", "bundle", "bundle root must be a real directory");
182
+ }
183
+ const canonicalRoot = realpathSync(absoluteRoot);
184
+ const bytes = readRegularFile(canonicalRoot, BUNDLE_MANIFEST_FILENAME, true);
185
+ let raw;
186
+ try {
187
+ raw = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
188
+ }
189
+ catch {
190
+ refuse("record-integrity", BUNDLE_MANIFEST_FILENAME, "bundle.json is not valid UTF-8 JSON");
191
+ }
192
+ const parsed = BundleManifestSchema.safeParse(raw);
193
+ if (!parsed.success) {
194
+ refuse("record-integrity", BUNDLE_MANIFEST_FILENAME, "bundle.json does not satisfy the manifest schema");
195
+ }
196
+ const canonical = canonicalJsonBytes(parsed.data);
197
+ if (!equalBytes(bytes, canonical)) {
198
+ refuse("record-integrity", BUNDLE_MANIFEST_FILENAME, "bundle.json bytes are not the exact canonical manifest encoding");
199
+ }
200
+ const seen = new Set();
201
+ let previous = "";
202
+ for (const entry of parsed.data.files) {
203
+ validateRelativePath(entry.path);
204
+ if (seen.has(entry.path)) {
205
+ refuse("record-integrity", "bundle.manifest.files", `duplicate bundle path "${entry.path}"`);
206
+ }
207
+ if (previous !== "" && entry.path <= previous) {
208
+ refuse("record-integrity", "bundle.manifest.files", "manifest files are not in strict path order");
209
+ }
210
+ seen.add(entry.path);
211
+ previous = entry.path;
212
+ const fileBytes = readRegularFile(canonicalRoot, entry.path);
213
+ if (fileBytes.length !== entry.bytes) {
214
+ refuse("record-integrity", entry.path, `byte length mismatch for "${entry.path}"`);
215
+ }
216
+ if (sha256(fileBytes) !== entry.sha256) {
217
+ refuse("record-integrity", entry.path, `digest mismatch for "${entry.path}"`);
218
+ }
219
+ }
220
+ const actual = new Set(walkTree(canonicalRoot));
221
+ const expected = new Set([BUNDLE_MANIFEST_FILENAME, ...seen]);
222
+ for (const path of expected) {
223
+ if (!actual.has(path))
224
+ refuse("record-integrity", path, `manifest entry "${path}" is missing`);
225
+ }
226
+ for (const path of actual) {
227
+ if (!expected.has(path))
228
+ refuse("record-integrity", path, `bundle contains unexpected file "${path}"`);
229
+ }
230
+ const fileBytes = new Map([[BUNDLE_MANIFEST_FILENAME, bytes]]);
231
+ for (const entry of parsed.data.files) {
232
+ const authenticated = readRegularFile(canonicalRoot, entry.path);
233
+ if (authenticated.length !== entry.bytes || sha256(authenticated) !== entry.sha256) {
234
+ refuse("record-integrity", entry.path, `manifest entry "${entry.path}" changed during snapshot authentication`);
235
+ }
236
+ fileBytes.set(entry.path, authenticated);
237
+ }
238
+ deps.afterManifestValidated?.();
239
+ return { manifest: parsed.data, bytes, identity: sha256(bytes), fileBytes };
240
+ }
@@ -0,0 +1,4 @@
1
+ /** Public bundle members required by the Colophon bundle profile. */
2
+ export declare const PUBLIC_BUNDLE_FILES: readonly ["static-bundle.json", "benchmark.json", "run.json", "matrix.json", "report.json", "report-envelope.json", "claim-package.json", "verdicts.json", "evidence.json", "verification/assembly.jsonl", "trust/public-keys.json", "index.html", "badge.svg", "social-card.svg", "README.md", "share.txt"];
3
+ /** V4 is the fixed v2 graph with exactly one canonical qualification projection added. */
4
+ export declare const PUBLIC_BUNDLE_V4_FILES: readonly ("benchmark.json" | "run.json" | "matrix.json" | "report.json" | "report-envelope.json" | "claim-package.json" | "qualification.json" | "static-bundle.json" | "evidence.json" | "verdicts.json" | "verification/assembly.jsonl" | "trust/public-keys.json" | "index.html" | "badge.svg" | "social-card.svg" | "README.md" | "share.txt")[];
@@ -0,0 +1,13 @@
1
+ /** Public bundle members required by the Colophon bundle profile. */
2
+ export const PUBLIC_BUNDLE_FILES = [
3
+ "static-bundle.json", "benchmark.json", "run.json", "matrix.json", "report.json",
4
+ "report-envelope.json", "claim-package.json", "verdicts.json", "evidence.json",
5
+ "verification/assembly.jsonl", "trust/public-keys.json", "index.html", "badge.svg",
6
+ "social-card.svg", "README.md", "share.txt",
7
+ ];
8
+ /** V4 is the fixed v2 graph with exactly one canonical qualification projection added. */
9
+ export const PUBLIC_BUNDLE_V4_FILES = [
10
+ ...PUBLIC_BUNDLE_FILES.slice(0, 7),
11
+ "qualification.json",
12
+ ...PUBLIC_BUNDLE_FILES.slice(7),
13
+ ];
@@ -0,0 +1,11 @@
1
+ export interface LocalAdmissionReceiptFact {
2
+ readonly zeroReplayVariance: boolean;
3
+ readonly externalCapabilities: boolean;
4
+ }
5
+ export interface ParsedLocalAdmissionReceipt {
6
+ readonly sha256: string;
7
+ readonly taskSha256: string;
8
+ readonly fact: LocalAdmissionReceiptFact;
9
+ }
10
+ /** Parses the public, sealed prediction-snapshot admission receipt. */
11
+ export declare function parsePredictionSnapshotAdmissionReceipt(bytes: Uint8Array, sha256?: string): ParsedLocalAdmissionReceipt | undefined;
@@ -0,0 +1,24 @@
1
+ import { ADMISSION_RECEIPT_MEDIA_TYPE, IN_TOTO_STATEMENT_TYPE, PREDICTION_SNAPSHOT_ADMISSION_POLICY_V1, } from "@jinn-network/task-admission";
2
+ import { parseDsseEnvelope } from "@jinn-network/trust-core";
3
+ /** Parses the public, sealed prediction-snapshot admission receipt. */
4
+ export function parsePredictionSnapshotAdmissionReceipt(bytes, sha256 = "") {
5
+ let envelope;
6
+ try {
7
+ envelope = parseDsseEnvelope(bytes);
8
+ }
9
+ catch {
10
+ return undefined;
11
+ }
12
+ if (envelope.payloadType !== ADMISSION_RECEIPT_MEDIA_TYPE)
13
+ return undefined;
14
+ try {
15
+ const statement = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(envelope.payloadBytes));
16
+ const digest = statement.predicate?.task?.documentDigest;
17
+ if (statement._type !== IN_TOTO_STATEMENT_TYPE || statement.predicateType !== PREDICTION_SNAPSHOT_ADMISSION_POLICY_V1.predicateType || typeof digest !== "string" || !/^sha256:[a-f0-9]{64}$/u.test(digest))
18
+ return undefined;
19
+ return { sha256, taskSha256: digest.slice("sha256:".length), fact: { zeroReplayVariance: true, externalCapabilities: false } };
20
+ }
21
+ catch {
22
+ return undefined;
23
+ }
24
+ }
@@ -0,0 +1,157 @@
1
+ /**
2
+ * The anchored-closure projection (anchor-evidence design §7.4 and §9.2): the pure function that
3
+ * turns a bundle's carried AnchorEvidence bytes into the claim package's `anchors` section, and
4
+ * the conditional honesty copy that section gates.
5
+ *
6
+ * This module is **single-sourced, not mirrored**. `@colophon-claims/core` already depends on this
7
+ * package (it takes `nodeCryptoAnchorPorts` and the reader instructions from here), so producer and
8
+ * verifier import the same function rather than two copies of it. §7.4 requires that "producer and
9
+ * verifier derive the section from the same bundle bytes by the same pure function, so
10
+ * claim-consistency remains an exact byte-compare" — one function is the strongest available form
11
+ * of that requirement, since two mirrored copies can drift and this one cannot.
12
+ *
13
+ * Four disciplines are load-bearing:
14
+ *
15
+ * - **Records are read only through `parseExactAnchorEvidence`, proof content only through
16
+ * `decodeAnchorProofContent`.** Conformance is shape-level and admits alternate JSON spellings;
17
+ * identity is byte-exactness. A record selected through the laxer door would have a digest other
18
+ * than the one it is filed under.
19
+ * - **Subject selectors are digest-keyed.** The lock anchor is any carried anchor whose
20
+ * `subject.digest` equals the digest of the bundle's exact `run.json` bytes; the matrix anchor is
21
+ * any whose digest equals `matrix.json`'s. `subject.kind` is then required to equal the resolved
22
+ * record's actual kind — it is checked, never used to route (§8 step 2). A digest that names no
23
+ * bundle record, or a kind that misdescribes the one it names, is `invalid`: affirmative evidence
24
+ * of substitution, reported louder than absence.
25
+ * - **Byte-embedded facts only.** `genTime`, `policyOid`, `serialNumber`,
26
+ * `signerCertificateSha256` for RFC 3161; the attested block height (or `pending`) for
27
+ * OpenTimestamps. Facts that need external data (block time) or lack a canonical rendering
28
+ * (issuer distinguished names, accuracy intervals) never enter this section — they are
29
+ * verifier-report and presentation content (§7.4).
30
+ * - **No trust material is supplied here.** This projection is a function of the bundle's bytes,
31
+ * never of a verifier's configured roots, because the sealed claim text must be identical for
32
+ * every reader (§8, "The honesty text keys on byte-facts"). Trust evaluation lives in the
33
+ * `integrity-anchors` check's own report.
34
+ */
35
+ import { z } from "zod";
36
+ /** The two records this product anchors (§7.1). Both are whole sealed-record digests. */
37
+ export type ClaimAnchorSubject = "lock" | "matrix";
38
+ /** RFC 3161's four byte-embedded facts (§7.4). `accuracy` and the issuer DN are deliberately
39
+ * absent: the first has no canonical single rendering, the second is not canonically comparable
40
+ * across implementations. */
41
+ export interface Rfc3161ClaimAnchorFacts {
42
+ readonly genTime: string;
43
+ readonly policyOid: string;
44
+ readonly serialNumber: string;
45
+ readonly signerCertificateSha256: string;
46
+ }
47
+ /** OpenTimestamps' one byte-embedded fact, or the calendar-only state. A `pending` proof carries no
48
+ * height because none has been attested yet — the two shapes are disjoint by construction so no
49
+ * reader can mistake a promise for a commitment (§13 item 5). */
50
+ export type OpenTimestampsClaimAnchorFacts = {
51
+ readonly blockHeight: number;
52
+ } | {
53
+ readonly pending: true;
54
+ };
55
+ export type ClaimAnchorFacts = Rfc3161ClaimAnchorFacts | OpenTimestampsClaimAnchorFacts;
56
+ export interface ClaimAnchor {
57
+ readonly subject: ClaimAnchorSubject;
58
+ /** The resolved record's actual kind IRI — checked against `subject.kind`, never taken from it. */
59
+ readonly kind: string;
60
+ readonly provider: string;
61
+ readonly recordSha256: string;
62
+ /** Present only on the §6.2 upgraded form of a pending proof — see `upgradeEdge` below. */
63
+ readonly upgradesRecordSha256?: string;
64
+ readonly facts: ClaimAnchorFacts;
65
+ }
66
+ export interface CarriedAnchorRecord {
67
+ readonly recordSha256: string;
68
+ readonly bytes: Uint8Array;
69
+ }
70
+ export declare const ClaimAnchorSchema: z.ZodObject<{
71
+ subject: z.ZodEnum<{
72
+ lock: "lock";
73
+ matrix: "matrix";
74
+ }>;
75
+ kind: z.ZodString;
76
+ provider: z.ZodString;
77
+ recordSha256: z.ZodString;
78
+ upgradesRecordSha256: z.ZodOptional<z.ZodString>;
79
+ facts: z.ZodUnion<readonly [z.ZodObject<{
80
+ genTime: z.ZodString;
81
+ policyOid: z.ZodString;
82
+ serialNumber: z.ZodString;
83
+ signerCertificateSha256: z.ZodString;
84
+ }, z.core.$strict>, z.ZodUnion<readonly [z.ZodObject<{
85
+ blockHeight: z.ZodNumber;
86
+ }, z.core.$strict>, z.ZodObject<{
87
+ pending: z.ZodLiteral<true>;
88
+ }, z.core.$strict>]>]>;
89
+ }, z.core.$strict>;
90
+ export interface DeriveClaimAnchorsInput {
91
+ readonly records: readonly CarriedAnchorRecord[];
92
+ /** sha256 hex of the bundle's exact `run.json` bytes. */
93
+ readonly runSha256: string;
94
+ /** sha256 hex of the bundle's exact `matrix.json` bytes. */
95
+ readonly matrixSha256: string;
96
+ }
97
+ /** A record this projection refuses. Every caller converts it into its own typed refusal
98
+ * (`record-integrity` on the producer side, an `invalid` anchor outcome on the verifier side), so
99
+ * the failure is never swallowed and never crosses a package boundary as an untyped throw. */
100
+ export declare class ClaimAnchorProjectionError extends Error {
101
+ readonly name = "ClaimAnchorProjectionError";
102
+ readonly recordSha256: string;
103
+ constructor(recordSha256: string, message: string);
104
+ }
105
+ /**
106
+ * Projects the carried AnchorEvidence records into the claim package's `anchors` section.
107
+ *
108
+ * Ordered by record digest so the section has exactly one spelling for one carried set. Throws
109
+ * `ClaimAnchorProjectionError` on a record that is not exactly-sealed, does not parse, names a
110
+ * digest no bundle record has, misdescribes the kind of the record it does name, carries a provider
111
+ * profile no verifier implements, or carries a proof the rules reject.
112
+ */
113
+ export declare function deriveClaimAnchors(input: DeriveClaimAnchorsInput): readonly ClaimAnchor[];
114
+ /** The historical value every unanchored bundle keeps (§9.2, "Additive"). */
115
+ export declare const STRUCTURAL_PRE_REGISTRATION: "structural-and-append-order-only";
116
+ /** The widened value an anchored bundle carries. */
117
+ export declare const ANCHORED_PRE_REGISTRATION: "structural-append-order-and-anchored-time";
118
+ export declare const SELF_RUN_TRUST_ROOT = "Signatures verify against the bundle-carried public keys minted by this workspace; there is no third-party trust anchor on the self-run venue.";
119
+ export declare const ANCHORED_TRUST_ROOT: string;
120
+ /**
121
+ * A lock anchor gates copy only when it is carried, structurally complete for its class, and
122
+ * digest- and kind-matching (§9.2). The first and last are guaranteed by `deriveClaimAnchors`,
123
+ * which refuses everything else; completeness is the one condition readable off the section: a
124
+ * `pending`-only proof is not structurally complete and gates nothing.
125
+ */
126
+ export declare function governingLockAnchors(anchors: readonly ClaimAnchor[]): readonly ClaimAnchor[];
127
+ /** Complete matrix anchors, in record-digest order. A matrix anchor upgrades nothing (§9.2). */
128
+ export declare function carriedMatrixAnchors(anchors: readonly ClaimAnchor[]): readonly ClaimAnchor[];
129
+ /**
130
+ * The replacement for limitation sentence 2, rendered once from the governing anchor.
131
+ *
132
+ * The `authority-time` form asserts, because its cryptography is fully carried and the verifier
133
+ * re-verifies it unconditionally and fails loud on `invalid`. The `chain-time` form is attributive
134
+ * — it says what the proof asserts and what checking it requires — because structural completeness
135
+ * is not chain evaluation; the assertive chain-time statement lives only in the report of a
136
+ * verifier that evaluated headers (§8, §9.2).
137
+ */
138
+ export declare function anchoredPreRegistrationSentence(governing: ClaimAnchor): string;
139
+ /** One neutral line per additional lock anchor (§9.2). It upgrades nothing; it reports. */
140
+ export declare function additionalLockAnchorLine(anchor: ClaimAnchor): string;
141
+ /** One neutral line per carried matrix anchor (§9.2). It upgrades no claim. */
142
+ export declare function matrixAnchorLine(anchor: ClaimAnchor): string;
143
+ /**
144
+ * The complete venue-limits list for one anchor section: the unconditional list with sentence 2
145
+ * replaced by the governing anchor's form, followed by the neutral lines. Sentences 1, 3/3', 4, and
146
+ * 5 are untouched — anchors have nothing to say about operator control, pinning axes, self-reported
147
+ * cost, or agent distinctness (§9.2).
148
+ *
149
+ * With no governing lock anchor the unconditional list is returned unchanged, including when the
150
+ * bundle carries only pending or only matrix anchors: absent, pending, or invalid anchors change
151
+ * nothing.
152
+ */
153
+ export declare function anchoredVenueLimits(unconditional: readonly string[], anchors: readonly ClaimAnchor[]): readonly string[];
154
+ /** `venueHonesty.preRegistration` for one anchor section (§9.2). */
155
+ export declare function anchoredPreRegistration(anchors: readonly ClaimAnchor[]): typeof STRUCTURAL_PRE_REGISTRATION | typeof ANCHORED_PRE_REGISTRATION;
156
+ /** `claim.verification.trustRoot` for one anchor section (§9.2). */
157
+ export declare function anchoredTrustRoot(anchors: readonly ClaimAnchor[]): string;