@forestrie/forestrie-cli 0.8.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 (124) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +425 -0
  3. package/TRUST-MODEL.md +8 -0
  4. package/dist/cli.js +33811 -0
  5. package/dist/decode-receipt.d.ts +19 -0
  6. package/dist/decode-receipt.d.ts.map +1 -0
  7. package/dist/decode-receipt.js +19 -0
  8. package/dist/decode-receipt.js.map +1 -0
  9. package/dist/index.d.ts +11 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +11 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/lib/decode-receipt-cbor.d.ts +26 -0
  14. package/dist/lib/decode-receipt-cbor.d.ts.map +1 -0
  15. package/dist/lib/decode-receipt-cbor.js +182 -0
  16. package/dist/lib/decode-receipt-cbor.js.map +1 -0
  17. package/dist/lib/decode-receipt-decode.d.ts +103 -0
  18. package/dist/lib/decode-receipt-decode.d.ts.map +1 -0
  19. package/dist/lib/decode-receipt-decode.js +237 -0
  20. package/dist/lib/decode-receipt-decode.js.map +1 -0
  21. package/dist/lib/decode-receipt-labels.d.ts +54 -0
  22. package/dist/lib/decode-receipt-labels.d.ts.map +1 -0
  23. package/dist/lib/decode-receipt-labels.js +117 -0
  24. package/dist/lib/decode-receipt-labels.js.map +1 -0
  25. package/dist/lib/decode-receipt-render.d.ts +11 -0
  26. package/dist/lib/decode-receipt-render.d.ts.map +1 -0
  27. package/dist/lib/decode-receipt-render.js +242 -0
  28. package/dist/lib/decode-receipt-render.js.map +1 -0
  29. package/package.json +83 -0
  30. package/src/cli.ts +5 -0
  31. package/src/command.ts +51 -0
  32. package/src/commands/admin-onboard-token.ts +51 -0
  33. package/src/commands/admin.ts +20 -0
  34. package/src/commands/complete-grant.ts +41 -0
  35. package/src/commands/create-consistency-proof.ts +44 -0
  36. package/src/commands/create-log.ts +95 -0
  37. package/src/commands/decode-receipt.ts +20 -0
  38. package/src/commands/delegate.ts +55 -0
  39. package/src/commands/deploy.ts +72 -0
  40. package/src/commands/fetch-accumulator.ts +49 -0
  41. package/src/commands/onboard-genesis.ts +70 -0
  42. package/src/commands/onboard-request.ts +59 -0
  43. package/src/commands/register-grant.ts +74 -0
  44. package/src/commands/register.ts +54 -0
  45. package/src/commands/resolve-receipt.ts +101 -0
  46. package/src/commands/sign-statement.ts +53 -0
  47. package/src/commands/verify-grant.ts +89 -0
  48. package/src/commands/verify.ts +92 -0
  49. package/src/commoncli.ts +34 -0
  50. package/src/decode-receipt.ts +56 -0
  51. package/src/index.ts +10 -0
  52. package/src/lib/bytes.ts +7 -0
  53. package/src/lib/checkpoint-provider.ts +297 -0
  54. package/src/lib/complete-grant-derive.ts +237 -0
  55. package/src/lib/consistency-proof.ts +271 -0
  56. package/src/lib/create-log-build.ts +168 -0
  57. package/src/lib/create-log-prepare.ts +75 -0
  58. package/src/lib/create-receipt-chain-verify.ts +267 -0
  59. package/src/lib/create-receipt-derive.ts +208 -0
  60. package/src/lib/create-receipt-inputs.ts +79 -0
  61. package/src/lib/decode-checkpoint-calldata.ts +188 -0
  62. package/src/lib/decode-receipt-cbor.ts +202 -0
  63. package/src/lib/decode-receipt-decode.ts +333 -0
  64. package/src/lib/decode-receipt-labels.ts +132 -0
  65. package/src/lib/decode-receipt-render.ts +284 -0
  66. package/src/lib/delegate-flow.ts +289 -0
  67. package/src/lib/delegate-voucher.ts +122 -0
  68. package/src/lib/deploy-artifact.ts +79 -0
  69. package/src/lib/deploy-bootstrap.ts +100 -0
  70. package/src/lib/deploy-flow.ts +184 -0
  71. package/src/lib/deploy-genesis-log-id.ts +29 -0
  72. package/src/lib/deploy-rpc.ts +40 -0
  73. package/src/lib/deployment-record.ts +65 -0
  74. package/src/lib/fsio.ts +91 -0
  75. package/src/lib/grant-flags-local.ts +19 -0
  76. package/src/lib/onboard-attestation.ts +120 -0
  77. package/src/lib/onboard-genesis-body.ts +94 -0
  78. package/src/lib/openssl-error-queue.ts +56 -0
  79. package/src/lib/register-flow.ts +292 -0
  80. package/src/lib/register-grant-build.ts +173 -0
  81. package/src/lib/register-grant-complete.ts +47 -0
  82. package/src/lib/register-grant-flow.ts +258 -0
  83. package/src/lib/resolve-receipt-freshen.ts +221 -0
  84. package/src/lib/sign-statement-build.ts +117 -0
  85. package/src/lib/sign-statement-key.ts +145 -0
  86. package/src/lib/verify-anchored.ts +326 -0
  87. package/src/lib/verify-checkpoint-chain.ts +160 -0
  88. package/src/lib/verify-eventscan.ts +214 -0
  89. package/src/lib/verify-inputs.ts +143 -0
  90. package/src/lib/verify-known-accumulator.ts +161 -0
  91. package/src/lib/verify-report.ts +265 -0
  92. package/src/lib/x402-payment.ts +115 -0
  93. package/src/main/admin-onboard-token.ts +216 -0
  94. package/src/main/complete-grant.ts +265 -0
  95. package/src/main/create-consistency-proof.ts +83 -0
  96. package/src/main/create-log.ts +347 -0
  97. package/src/main/create-receipt.ts +763 -0
  98. package/src/main/decode-receipt.ts +72 -0
  99. package/src/main/delegate.ts +141 -0
  100. package/src/main/deploy.ts +273 -0
  101. package/src/main/fetch-accumulator.ts +181 -0
  102. package/src/main/not-implemented.ts +43 -0
  103. package/src/main/onboard-genesis.ts +192 -0
  104. package/src/main/onboard-request.ts +333 -0
  105. package/src/main/register-grant.ts +270 -0
  106. package/src/main/register.ts +243 -0
  107. package/src/main/sign-statement.ts +168 -0
  108. package/src/main/verify.ts +767 -0
  109. package/src/options/admin-onboard-token.ts +48 -0
  110. package/src/options/common.ts +69 -0
  111. package/src/options/complete-grant.ts +40 -0
  112. package/src/options/create-consistency-proof.ts +63 -0
  113. package/src/options/create-log.ts +99 -0
  114. package/src/options/create-receipt.ts +243 -0
  115. package/src/options/decode-receipt.ts +25 -0
  116. package/src/options/delegate.ts +93 -0
  117. package/src/options/deploy.ts +105 -0
  118. package/src/options/fetch-accumulator.ts +62 -0
  119. package/src/options/onboard-genesis.ts +74 -0
  120. package/src/options/onboard-request.ts +57 -0
  121. package/src/options/register-grant.ts +72 -0
  122. package/src/options/register.ts +71 -0
  123. package/src/options/sign-statement.ts +64 -0
  124. package/src/options/verify.ts +250 -0
@@ -0,0 +1,333 @@
1
+ /**
2
+ * Structural decode for `forestrie decode-receipt` (FOR-346).
3
+ *
4
+ * `parseReceipt` (`@forestrie/receipt-verify`) does the load-bearing
5
+ * receipt parse (COSE_Sign1 shape, tag-18 tolerance, payload rules,
6
+ * header 396 inclusion proof). This module turns that result into a
7
+ * display model: protected header contents (which `parseReceipt` leaves
8
+ * as opaque bstr), named labels, and JSON-safe values. Unknown labels
9
+ * are carried through raw — never dropped.
10
+ */
11
+
12
+ import { parseReceipt } from "@forestrie/receipt-verify";
13
+ import { coseUnprotectedToMap, decodeCoseSign1 } from "@forestrie/encoding";
14
+
15
+ import {
16
+ CborDecodeError,
17
+ decodeCborMap,
18
+ isCborTagged,
19
+ type CborValue,
20
+ } from "./decode-receipt-cbor.js";
21
+ import {
22
+ ALG_NAMES,
23
+ COSE_SIGN1_TAG,
24
+ CWT_CLAIMS_LABEL,
25
+ CWT_CLAIM_NAMES,
26
+ COSE_KEY_PARAM_NAMES,
27
+ DELEGATION_CERT_LABEL,
28
+ SEAL_PEAK_RECEIPTS_LABEL,
29
+ VDS_LABEL,
30
+ VDS_NAMES,
31
+ headerLabelInfo,
32
+ } from "./decode-receipt-labels.js";
33
+
34
+ /** Parse stage names surfaced in structured errors. */
35
+ export type DecodeReceiptStage =
36
+ | "input"
37
+ | "envelope"
38
+ | "cose-sign1"
39
+ | "payload"
40
+ | "protected-header"
41
+ | "inclusion-proof";
42
+
43
+ /** Structured decode failure: which parse stage rejected the input. */
44
+ export class DecodeReceiptError extends Error {
45
+ readonly stage: DecodeReceiptStage;
46
+
47
+ constructor(stage: DecodeReceiptStage, message: string) {
48
+ super(message);
49
+ this.name = "DecodeReceiptError";
50
+ this.stage = stage;
51
+ }
52
+ }
53
+
54
+ /** JSON-safe value: bytes become `h'…'` diagnostic-notation strings. */
55
+ export type Json =
56
+ | string
57
+ | number
58
+ | boolean
59
+ | null
60
+ | Json[]
61
+ | { [key: string]: Json };
62
+
63
+ export type DecodedHeaderEntry = {
64
+ /** Raw CBOR label (int, or string for text keys). */
65
+ label: number | string;
66
+ /** Registry name, or null when the label is unknown (shown raw). */
67
+ name: string | null;
68
+ /** Registry provenance note, when known. */
69
+ note: string | null;
70
+ /** Full-fidelity value (bytes as h'…'). */
71
+ value: Json;
72
+ };
73
+
74
+ export type DecodedClaim = {
75
+ key: number | string;
76
+ name: string | null;
77
+ value: Json;
78
+ };
79
+
80
+ export type DecodedReceipt = {
81
+ /** Total receipt size in bytes. */
82
+ byteLength: number;
83
+ /** Outer CBOR tag (18 for COSE_Sign1) or null when untagged. */
84
+ tag: number | null;
85
+ protected: {
86
+ byteLength: number;
87
+ alg: { value: number; name: string | null } | null;
88
+ kid: { hex: string; byteLength: number } | { text: string } | null;
89
+ vds: { value: number; name: string | null } | null;
90
+ /** CWT claims (header 15), incl. any cnf / ephemeral key material. */
91
+ cwtClaims: DecodedClaim[] | null;
92
+ entries: DecodedHeaderEntry[];
93
+ };
94
+ unprotected: {
95
+ entries: DecodedHeaderEntry[];
96
+ delegation: { byteLength: number; nestedCoseSign1: boolean } | null;
97
+ /** Present when this is a checkpoint carrying pre-signed peak receipts. */
98
+ peakReceipts: { count: number } | null;
99
+ };
100
+ payload:
101
+ | { detached: true }
102
+ | { detached: false; byteLength: number; hex: string };
103
+ signature: { byteLength: number; hex: string };
104
+ /** MMR inclusion proof summary (header 396, key -1). */
105
+ inclusion: {
106
+ mmrIndex: string;
107
+ pathLength: number;
108
+ path: string[];
109
+ /** 32-byte peak hash when the payload is attached; null when detached. */
110
+ peakHex: string | null;
111
+ peakSource: "payload" | "derived at verify time (detached payload)";
112
+ };
113
+ };
114
+
115
+ const HEX = Array.from({ length: 256 }, (_, i) =>
116
+ i.toString(16).padStart(2, "0"),
117
+ );
118
+
119
+ export function bytesToHex(bytes: Uint8Array): string {
120
+ let out = "";
121
+ for (const b of bytes) out += HEX[b];
122
+ return out;
123
+ }
124
+
125
+ /** Convert any decoded CBOR / cbor-x value to JSON-safe display form. */
126
+ export function toJson(value: unknown): Json {
127
+ if (value === null || value === undefined) return null;
128
+ if (typeof value === "boolean" || typeof value === "string") return value;
129
+ if (typeof value === "number") {
130
+ return Number.isFinite(value) ? value : String(value);
131
+ }
132
+ if (typeof value === "bigint") {
133
+ return value >= BigInt(Number.MIN_SAFE_INTEGER) &&
134
+ value <= BigInt(Number.MAX_SAFE_INTEGER)
135
+ ? Number(value)
136
+ : value.toString(10);
137
+ }
138
+ if (value instanceof Uint8Array) return `h'${bytesToHex(value)}'`;
139
+ if (Array.isArray(value)) return value.map(toJson);
140
+ if (value instanceof Map) {
141
+ const out: { [key: string]: Json } = {};
142
+ for (const [k, v] of value) out[String(toJson(k))] = toJson(v);
143
+ return out;
144
+ }
145
+ if (isCborTagged(value)) {
146
+ return { tag: value.tag, value: toJson(value.value) };
147
+ }
148
+ if (typeof value === "object") {
149
+ const out: { [key: string]: Json } = {};
150
+ for (const [k, v] of Object.entries(value)) out[k] = toJson(v);
151
+ return out;
152
+ }
153
+ return String(value);
154
+ }
155
+
156
+ function numericLabel(key: unknown): number | string {
157
+ if (typeof key === "number") return key;
158
+ if (typeof key === "bigint") return Number(key);
159
+ const n = Number(key);
160
+ return Number.isFinite(n) ? n : String(key);
161
+ }
162
+
163
+ function headerEntry(key: unknown, value: unknown): DecodedHeaderEntry {
164
+ const label = numericLabel(key);
165
+ const info = typeof label === "number" ? headerLabelInfo(label) : null;
166
+ return {
167
+ label,
168
+ name: info?.name ?? null,
169
+ note: info?.note ?? null,
170
+ value: toJson(value),
171
+ };
172
+ }
173
+
174
+ function decodeCwtClaims(claims: CborValue): DecodedClaim[] {
175
+ if (!(claims instanceof Map)) {
176
+ return [{ key: "(malformed)", name: null, value: toJson(claims) }];
177
+ }
178
+ const out: DecodedClaim[] = [];
179
+ for (const [k, v] of claims) {
180
+ const key = numericLabel(k);
181
+ const name =
182
+ typeof key === "number" ? (CWT_CLAIM_NAMES.get(key) ?? null) : null;
183
+ // cnf (8) carries key material — name COSE_Key params for the audience.
184
+ if (key === 8 && v instanceof Map) {
185
+ const cnf: { [param: string]: Json } = {};
186
+ for (const [pk, pv] of v) {
187
+ const paramKey = numericLabel(pk);
188
+ const paramName =
189
+ typeof paramKey === "number"
190
+ ? COSE_KEY_PARAM_NAMES.get(paramKey)
191
+ : undefined;
192
+ cnf[paramName ? `${paramKey} (${paramName})` : String(paramKey)] =
193
+ toJson(pv);
194
+ }
195
+ out.push({ key, name, value: cnf });
196
+ continue;
197
+ }
198
+ out.push({ key, name, value: toJson(v) });
199
+ }
200
+ return out;
201
+ }
202
+
203
+ /** Classify a `parseReceipt` failure by the stage that rejected the input. */
204
+ function classifyParseReceiptError(error: unknown): DecodeReceiptError {
205
+ const message = error instanceof Error ? error.message : String(error);
206
+ if (/COSE Sign1/i.test(message)) {
207
+ return new DecodeReceiptError("cose-sign1", message);
208
+ }
209
+ if (/payload/i.test(message)) {
210
+ return new DecodeReceiptError("payload", message);
211
+ }
212
+ if (/header 396|proof/i.test(message)) {
213
+ return new DecodeReceiptError("inclusion-proof", message);
214
+ }
215
+ // Anything else came out of the CBOR envelope decode.
216
+ return new DecodeReceiptError("envelope", message);
217
+ }
218
+
219
+ /**
220
+ * Decode receipt bytes to the display model.
221
+ *
222
+ * @throws DecodeReceiptError naming the parse stage on malformed input
223
+ */
224
+ export function decodeReceipt(receiptBytes: Uint8Array): DecodedReceipt {
225
+ if (receiptBytes.length === 0) {
226
+ throw new DecodeReceiptError("input", "receipt is empty (0 bytes)");
227
+ }
228
+
229
+ // Tag tolerance: parseReceipt accepts both; record which form we got.
230
+ // Tag 18 with a 1-byte argument encodes as the initial byte 0xd2.
231
+ const tag = receiptBytes[0] === 0xd2 ? COSE_SIGN1_TAG : null;
232
+
233
+ let parsed: ReturnType<typeof parseReceipt>;
234
+ try {
235
+ parsed = parseReceipt(receiptBytes);
236
+ } catch (error) {
237
+ throw classifyParseReceiptError(error);
238
+ }
239
+ const [protectedBstr, unprotectedRaw, payload, signature] = parsed.coseSign1;
240
+
241
+ // Protected header: parseReceipt keeps it opaque (signed bytes); open it.
242
+ let protectedMap: Map<CborValue, CborValue>;
243
+ try {
244
+ protectedMap = decodeCborMap(protectedBstr);
245
+ } catch (error) {
246
+ const message =
247
+ error instanceof CborDecodeError
248
+ ? `protected header is not a valid CBOR map: ${error.message}`
249
+ : String(error);
250
+ throw new DecodeReceiptError("protected-header", message);
251
+ }
252
+
253
+ let alg: DecodedReceipt["protected"]["alg"] = null;
254
+ let kid: DecodedReceipt["protected"]["kid"] = null;
255
+ let vds: DecodedReceipt["protected"]["vds"] = null;
256
+ let cwtClaims: DecodedClaim[] | null = null;
257
+ const protectedEntries: DecodedHeaderEntry[] = [];
258
+ for (const [k, v] of protectedMap) {
259
+ protectedEntries.push(headerEntry(k, v));
260
+ const label = numericLabel(k);
261
+ if (label === 1 && (typeof v === "number" || typeof v === "bigint")) {
262
+ const value = Number(v);
263
+ alg = { value, name: ALG_NAMES.get(value) ?? null };
264
+ } else if (label === 4) {
265
+ if (v instanceof Uint8Array) {
266
+ kid = { hex: bytesToHex(v), byteLength: v.length };
267
+ } else if (typeof v === "string") {
268
+ kid = { text: v };
269
+ }
270
+ } else if (
271
+ label === VDS_LABEL &&
272
+ (typeof v === "number" || typeof v === "bigint")
273
+ ) {
274
+ const value = Number(v);
275
+ vds = { value, name: VDS_NAMES.get(value) ?? null };
276
+ } else if (label === CWT_CLAIMS_LABEL) {
277
+ cwtClaims = decodeCwtClaims(v as CborValue);
278
+ }
279
+ }
280
+
281
+ // Unprotected header: already decoded by parseReceipt's CBOR pass.
282
+ const unprotectedMap = coseUnprotectedToMap(unprotectedRaw);
283
+ const unprotectedEntries: DecodedHeaderEntry[] = [];
284
+ let delegation: DecodedReceipt["unprotected"]["delegation"] = null;
285
+ let peakReceipts: DecodedReceipt["unprotected"]["peakReceipts"] = null;
286
+ for (const [label, value] of unprotectedMap) {
287
+ unprotectedEntries.push(headerEntry(label, value));
288
+ if (label === DELEGATION_CERT_LABEL && value instanceof Uint8Array) {
289
+ delegation = {
290
+ byteLength: value.length,
291
+ nestedCoseSign1: decodeCoseSign1(value) !== null,
292
+ };
293
+ } else if (label === SEAL_PEAK_RECEIPTS_LABEL && Array.isArray(value)) {
294
+ peakReceipts = { count: value.length };
295
+ }
296
+ }
297
+
298
+ const path = parsed.proof.path.map(bytesToHex);
299
+ const peakHex = parsed.explicitPeak ? bytesToHex(parsed.explicitPeak) : null;
300
+
301
+ return {
302
+ byteLength: receiptBytes.length,
303
+ tag,
304
+ protected: {
305
+ byteLength: protectedBstr.length,
306
+ alg,
307
+ kid,
308
+ vds,
309
+ cwtClaims,
310
+ entries: protectedEntries,
311
+ },
312
+ unprotected: {
313
+ entries: unprotectedEntries,
314
+ delegation,
315
+ peakReceipts,
316
+ },
317
+ payload:
318
+ payload instanceof Uint8Array
319
+ ? { detached: false, byteLength: payload.length, hex: bytesToHex(payload) }
320
+ : { detached: true },
321
+ signature: { byteLength: signature.length, hex: bytesToHex(signature) },
322
+ inclusion: {
323
+ // parseReceipt always sets mmrIndex; Proof marks it optional.
324
+ mmrIndex: (parsed.proof.mmrIndex ?? 0n).toString(10),
325
+ pathLength: path.length,
326
+ path,
327
+ peakHex,
328
+ peakSource: peakHex
329
+ ? "payload"
330
+ : "derived at verify time (detached payload)",
331
+ },
332
+ };
333
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * COSE / forestrie header label registry for `forestrie decode-receipt`
3
+ * (FOR-346). Naming only — decoding never requires a label to be known,
4
+ * and unknown labels are always shown raw, never dropped.
5
+ *
6
+ * Sources: RFC 9052 (COSE headers), RFC 9597 (CWT claims header 15),
7
+ * RFC 8392 (CWT claim keys), draft-ietf-cose-merkle-tree-proofs
8
+ * (395 vds / 396 verifiable proofs), and the forestrie private-use
9
+ * labels (canopy grants.md, ADR-0046, plan-0033 format v3,
10
+ * `@forestrie/receipt-verify` forest-genesis-labels).
11
+ */
12
+
13
+ /** CBOR tag for COSE_Sign1 (RFC 9052 §2). */
14
+ export const COSE_SIGN1_TAG = 18;
15
+
16
+ /** Verifiable proofs unprotected header (draft-ietf-cose-merkle-tree-proofs). */
17
+ export const VERIFIABLE_PROOFS_LABEL = 396;
18
+ /** Verifiable data structure protected header (draft-ietf-cose-merkle-tree-proofs). */
19
+ export const VDS_LABEL = 395;
20
+ /** CWT claims in a COSE header (RFC 9597). */
21
+ export const CWT_CLAIMS_LABEL = 15;
22
+ /** Delegation certificate — Custodian per-log delegation, nested COSE_Sign1. */
23
+ export const DELEGATION_CERT_LABEL = 1000;
24
+ /** Pre-signed peak inclusion receipts on a checkpoint (`SealPeakReceiptsLabel`). */
25
+ export const SEAL_PEAK_RECEIPTS_LABEL = -65931;
26
+
27
+ /** Inclusion proofs key inside header 396. */
28
+ export const PROOFS_INCLUSION_KEY = -1;
29
+ /** Consistency proofs key inside header 396. */
30
+ export const PROOFS_CONSISTENCY_KEY = -2;
31
+
32
+ export type LabelInfo = {
33
+ name: string;
34
+ /** Short provenance / meaning note for human output. */
35
+ note?: string;
36
+ };
37
+
38
+ /** COSE header labels (protected and unprotected share the number space). */
39
+ export const HEADER_LABELS: ReadonlyMap<number, LabelInfo> = new Map([
40
+ [1, { name: "alg" }],
41
+ [2, { name: "crit" }],
42
+ [3, { name: "content type" }],
43
+ [4, { name: "kid" }],
44
+ [5, { name: "IV" }],
45
+ [6, { name: "partial IV" }],
46
+ [CWT_CLAIMS_LABEL, { name: "CWT claims", note: "RFC 9597" }],
47
+ [
48
+ VDS_LABEL,
49
+ { name: "verifiable data structure", note: "COSE receipts (draft)" },
50
+ ],
51
+ [
52
+ VERIFIABLE_PROOFS_LABEL,
53
+ { name: "verifiable proofs", note: "COSE receipts (draft)" },
54
+ ],
55
+ [
56
+ DELEGATION_CERT_LABEL,
57
+ {
58
+ name: "delegation certificate",
59
+ note: "forestrie: Custodian per-log delegation (nested COSE_Sign1)",
60
+ },
61
+ ],
62
+ [-65537, { name: "idtimestamp", note: "forestrie private-use" }],
63
+ [-65538, { name: "forestrie grant v0", note: "forestrie private-use" }],
64
+ [
65
+ SEAL_PEAK_RECEIPTS_LABEL,
66
+ {
67
+ name: "pre-signed peak receipts",
68
+ note: "forestrie SealPeakReceiptsLabel (checkpoint header)",
69
+ },
70
+ ],
71
+ [-68009, { name: "forest genesis version", note: "forestrie private-use" }],
72
+ [-68011, { name: "univocity address", note: "forestrie private-use" }],
73
+ [-68013, { name: "chain id", note: "forestrie private-use" }],
74
+ [-68014, { name: "forest genesis alg", note: "forestrie private-use" }],
75
+ [-68015, { name: "bootstrap key", note: "forestrie private-use" }],
76
+ ]);
77
+
78
+ /** COSE algorithm names (RFC 9053 + forestrie private-use). */
79
+ export const ALG_NAMES: ReadonlyMap<number, string> = new Map([
80
+ [-7, "ES256 (ECDSA P-256 + SHA-256)"],
81
+ [-8, "EdDSA"],
82
+ [-35, "ES384"],
83
+ [-36, "ES512"],
84
+ [-65799, "KS256 (secp256k1 + Keccak-256, forestrie private-use)"],
85
+ ]);
86
+
87
+ /**
88
+ * Verifiable data structure ids (draft-ietf-cose-merkle-tree-proofs
89
+ * registry). 3 is NOT a registered codepoint: draft-bryce-cose-merkle-
90
+ * mountain-range-proofs requests TBD_1 (registration pending), and 3 is
91
+ * only the value our test fixtures use — production emitters do not emit
92
+ * header 395 at all. Render it as the draft's unregistered codepoint,
93
+ * never as registry fact (F7, plan-2607-14 W1.5).
94
+ */
95
+ export const VDS_NAMES: ReadonlyMap<number, string> = new Map([
96
+ [1, "RFC9162_SHA256 (Certificate Transparency)"],
97
+ [2, "CCF_LEDGER_SHA256"],
98
+ [3, "MMR profile (draft-bryce, codepoint TBD)"],
99
+ ]);
100
+
101
+ /** CWT claim keys (RFC 8392 §3.1, cnf per RFC 8747). */
102
+ export const CWT_CLAIM_NAMES: ReadonlyMap<number, string> = new Map([
103
+ [1, "iss"],
104
+ [2, "sub"],
105
+ [3, "aud"],
106
+ [4, "exp"],
107
+ [5, "nbf"],
108
+ [6, "iat"],
109
+ [7, "cti"],
110
+ [8, "cnf (confirmation / ephemeral key)"],
111
+ ]);
112
+
113
+ /** COSE_Key parameter names (RFC 9052 §7) — for cnf / ephemeral keys. */
114
+ export const COSE_KEY_PARAM_NAMES: ReadonlyMap<number, string> = new Map([
115
+ [1, "kty"],
116
+ [2, "kid"],
117
+ [3, "alg"],
118
+ [-1, "crv"],
119
+ [-2, "x"],
120
+ [-3, "y"],
121
+ ]);
122
+
123
+ /** Keys inside header 396 (draft-ietf-cose-merkle-tree-proofs). */
124
+ export const PROOF_KIND_NAMES: ReadonlyMap<number, string> = new Map([
125
+ [PROOFS_INCLUSION_KEY, "inclusion proofs"],
126
+ [PROOFS_CONSISTENCY_KEY, "consistency proofs"],
127
+ ]);
128
+
129
+ /** Look up a header label name; null when unknown (caller shows it raw). */
130
+ export function headerLabelInfo(label: number): LabelInfo | null {
131
+ return HEADER_LABELS.get(label) ?? null;
132
+ }