@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,250 @@
1
+ import type { LooseParsedArgs } from "@forestrie/cli-kit";
2
+ import {
3
+ optionalStringOption,
4
+ parseForestrieCommonOptions,
5
+ requiredStringOption,
6
+ type ForestrieCommonOptions,
7
+ } from "./common.js";
8
+
9
+ /**
10
+ * `forestrie verify` / `verify-grant` — FOR-347.
11
+ *
12
+ * Offline verification against a cached checkpoint — no network during the core
13
+ * verify. Every receipt is a standard COSE Receipt (MMR profile); the two
14
+ * commands differ only in how the leaf ContentHash payload is obtained:
15
+ *
16
+ * - `verify` (this file, {@link VerifyOptions}): the generic, SCITT-compatible
17
+ * path. The caller supplies the EXACT registered payload (`--payload`, e.g. a
18
+ * signed statement COSE); the leaf commits `SHA-256(idtimestamp ‖ SHA-256(payload))`.
19
+ * - `verify-grant` ({@link VerifyGrantOptions}): a thin wrapper for forestrie
20
+ * authority grants — it derives the grant commitment preimage from a
21
+ * structured grant and verifies it as the payload.
22
+ *
23
+ * Both add `--univocity --log-id --rpc-url` for the chain-anchored check (the
24
+ * only networked path).
25
+ *
26
+ * Trust anchors (FOR-297 ladder; each rung needs strictly less trust):
27
+ * 1. `--known-log-key` — caller-known log OWNER key: offline, no genesis;
28
+ * the key↔log binding is asserted by the key's provenance, not proven;
29
+ * no lifecycle visibility, no split-view protection.
30
+ * 2. `--genesis` — genesis-derived roots (the grant-chain walk, approach A,
31
+ * will derive per-log bindings from genesis + public tiles).
32
+ * 3. `--known-accumulator` — cached, auditable chain read: contract-enforced
33
+ * state (signature + grant chain + split-view for covered entries),
34
+ * fully offline; `--massif` (tiles) or `--consistency-proof` (portable
35
+ * top-up artifact) extends older receipts to a newer snapshot.
36
+ * 4. `--checkpoint-chain` — retained `.sth` fold (FOR-368): authenticated
37
+ * accumulator at every retained seal from the public log store alone;
38
+ * signatures root in rung 1/2 trust.
39
+ * 5. `--rpc-url` — live chain read: rung 3 plus freshness (the RPC provider
40
+ * is itself a trusted chain reader); buried peaks resolve via the
41
+ * CheckpointPublished history scan (public chain data only).
42
+ */
43
+
44
+ type AnchorFields = {
45
+ anchor: "offline" | "chain" | "accumulator" | "checkpoints";
46
+ univocity: string | undefined;
47
+ logId: string | undefined;
48
+ rpcUrl: string | undefined;
49
+ /**
50
+ * Cached on-chain accumulator snapshot (FOR-297 D5) — chain-anchored
51
+ * verification without `--rpc-url`. Produced by `forestrie
52
+ * fetch-accumulator`; never source it unauthenticated from the log
53
+ * operator's tile store.
54
+ */
55
+ knownAccumulator: string | undefined;
56
+ /** Local massif blob enabling stale-snapshot proof-path extension. */
57
+ massif: string | undefined;
58
+ /**
59
+ * Retained `.sth` checkpoint chain (FOR-368 Phase 3): a directory of
60
+ * `.sth` objects or a comma-separated list in chain order. Folding the
61
+ * chain authenticates the accumulator at every retained seal from the
62
+ * public log store alone — no tiles, no RPC. Signature trust roots in
63
+ * `--genesis` / `--known-log-key`.
64
+ */
65
+ checkpointChain: string | undefined;
66
+ /**
67
+ * Portable top-up artifact (`forestrie create-consistency-proof`,
68
+ * FOR-368 Phase 3): tile-free extension of an old receipt to the
69
+ * `--known-accumulator` snapshot. Unsigned and untrusted — soundness is
70
+ * by recomputation into the trusted snapshot.
71
+ */
72
+ consistencyProof: string | undefined;
73
+ /** Lower bound for the CheckpointPublished history scan (FOR-368). */
74
+ fromBlock: bigint | undefined;
75
+ /**
76
+ * Caller-known log OWNER key (the delegation-cert issuer), base64 `x||y`
77
+ * (64 bytes, `KNOWN_LOG_KEY`) — FOR-297 D1. An offline trust anchor that
78
+ * replaces the genesis-derived roots: the "known hosts" rung of the trust
79
+ * ladder. It asserts (does not prove) the key↔log binding, and gives no
80
+ * grant-lifecycle visibility or split-view protection — the grant-chain
81
+ * walk (approach A) derives the binding; chain anchors add split-view.
82
+ */
83
+ knownLogKey: string | undefined;
84
+ };
85
+
86
+ function parseAnchorFields(args: LooseParsedArgs): AnchorFields {
87
+ const univocity = optionalStringOption(args, "univocity");
88
+ const logId = optionalStringOption(args, "log-id");
89
+ const rpcUrl = optionalStringOption(args, "rpc-url", "RPC_URL");
90
+ const knownLogKey = optionalStringOption(args, "known-log-key", "KNOWN_LOG_KEY");
91
+ const knownAccumulator = optionalStringOption(args, "known-accumulator");
92
+ const massif = optionalStringOption(args, "massif");
93
+ const checkpointChain = optionalStringOption(args, "checkpoint-chain");
94
+ const consistencyProof = optionalStringOption(args, "consistency-proof");
95
+ const fromBlockRaw = optionalStringOption(args, "from-block");
96
+ let fromBlock: bigint | undefined;
97
+ if (fromBlockRaw !== undefined) {
98
+ try {
99
+ fromBlock = BigInt(fromBlockRaw);
100
+ } catch {
101
+ throw new Error("--from-block must be a block number");
102
+ }
103
+ if (fromBlock < 0n) {
104
+ throw new Error("--from-block must be a non-negative block number");
105
+ }
106
+ }
107
+ // Anchor modes are mutually exclusive. Conflict keys on --univocity (the
108
+ // live-read mode selector), NOT on rpcUrl: RPC_URL is commonly ambient in
109
+ // the environment and must not block the offline anchors.
110
+ const selected = [
111
+ knownAccumulator !== undefined ? "--known-accumulator" : null,
112
+ univocity !== undefined ? "--univocity" : null,
113
+ checkpointChain !== undefined ? "--checkpoint-chain" : null,
114
+ ].filter((s) => s !== null);
115
+ if (selected.length > 1) {
116
+ throw new Error(
117
+ `choose one anchor: a live read (--univocity/--log-id/--rpc-url), a cached --known-accumulator, or a retained --checkpoint-chain (got ${selected.join(" + ")})`,
118
+ );
119
+ }
120
+ let anchor: AnchorFields["anchor"] = "offline";
121
+ if (knownAccumulator !== undefined) {
122
+ anchor = "accumulator";
123
+ } else if (univocity !== undefined) {
124
+ if (logId === undefined || rpcUrl === undefined) {
125
+ throw new Error(
126
+ "chain-anchored verify requires --univocity, --log-id and --rpc-url",
127
+ );
128
+ }
129
+ anchor = "chain";
130
+ } else if (checkpointChain !== undefined) {
131
+ anchor = "checkpoints";
132
+ } else if (massif !== undefined) {
133
+ throw new Error("--massif only applies with --known-accumulator");
134
+ }
135
+ if (massif !== undefined && anchor !== "accumulator") {
136
+ throw new Error("--massif only applies with --known-accumulator");
137
+ }
138
+ if (consistencyProof !== undefined && anchor !== "accumulator") {
139
+ throw new Error(
140
+ "--consistency-proof only applies with --known-accumulator (the trusted target state)",
141
+ );
142
+ }
143
+ if (fromBlock !== undefined && anchor !== "chain") {
144
+ throw new Error(
145
+ "--from-block only applies to the live chain anchor (--univocity)",
146
+ );
147
+ }
148
+ return {
149
+ anchor,
150
+ univocity,
151
+ logId,
152
+ rpcUrl,
153
+ knownLogKey,
154
+ knownAccumulator,
155
+ massif,
156
+ checkpointChain,
157
+ consistencyProof,
158
+ fromBlock,
159
+ };
160
+ }
161
+
162
+ /** `--genesis` is only optional when another trust anchor is supplied. */
163
+ function requiredTrustAnchor(
164
+ args: LooseParsedArgs,
165
+ knownLogKey: string | undefined,
166
+ ): string | undefined {
167
+ const genesis = optionalStringOption(args, "genesis");
168
+ if (genesis === undefined && knownLogKey === undefined) {
169
+ throw new Error(
170
+ "a trust anchor is required: --genesis (genesis-derived roots) or --known-log-key (caller-known log owner key)",
171
+ );
172
+ }
173
+ return genesis;
174
+ }
175
+
176
+ // ---------------------------------------------------------------------------
177
+ // verify (generic, payload)
178
+ // ---------------------------------------------------------------------------
179
+
180
+ export type VerifyOptions = ForestrieCommonOptions &
181
+ AnchorFields & {
182
+ /** Cached public genesis (genesis.cbor) — the genesis-derived trust
183
+ * anchor. Optional when `--known-log-key` supplies the anchor instead. */
184
+ genesis: string | undefined;
185
+ /** COSE receipt file to verify. */
186
+ receipt: string;
187
+ /** The EXACT registered payload (leaf commits SHA-256 of these bytes). */
188
+ payload: string;
189
+ /** SCRAPI entry id — supplies the leaf idtimestamp. */
190
+ entryId: string;
191
+ };
192
+
193
+ export function parseVerifyOptions(args: LooseParsedArgs): VerifyOptions {
194
+ const anchorFields = parseAnchorFields(args);
195
+ const options: VerifyOptions = {
196
+ ...parseForestrieCommonOptions(args),
197
+ ...anchorFields,
198
+ genesis: requiredTrustAnchor(args, anchorFields.knownLogKey),
199
+ receipt: requiredStringOption(args, "receipt"),
200
+ payload: requiredStringOption(args, "payload"),
201
+ entryId: requiredStringOption(args, "entry-id"),
202
+ };
203
+ return options;
204
+ }
205
+
206
+ // ---------------------------------------------------------------------------
207
+ // verify-grant (wraps: derives the grant commitment payload)
208
+ // ---------------------------------------------------------------------------
209
+
210
+ export type VerifyGrantOptions = ForestrieCommonOptions &
211
+ AnchorFields & {
212
+ genesis: string | undefined;
213
+ receipt: string;
214
+ /** Completed grant credential, base64 (env GRANT_B64). */
215
+ committedGrant: string | undefined;
216
+ /** Grant CBOR file (alternative to --committed-grant). */
217
+ committedGrantFile: string | undefined;
218
+ /** Entry id within the grant CBOR (used with --committed-grant-file). */
219
+ entryId: string | undefined;
220
+ };
221
+
222
+ export function parseVerifyGrantOptions(
223
+ args: LooseParsedArgs,
224
+ ): VerifyGrantOptions {
225
+ const anchorFields = parseAnchorFields(args);
226
+ const options: VerifyGrantOptions = {
227
+ ...parseForestrieCommonOptions(args),
228
+ ...anchorFields,
229
+ genesis: requiredTrustAnchor(args, anchorFields.knownLogKey),
230
+ receipt: requiredStringOption(args, "receipt"),
231
+ committedGrant: optionalStringOption(args, "committed-grant", "GRANT_B64"),
232
+ committedGrantFile: optionalStringOption(args, "committed-grant-file"),
233
+ entryId: optionalStringOption(args, "entry-id"),
234
+ };
235
+ if (
236
+ options.committedGrant === undefined &&
237
+ options.committedGrantFile === undefined
238
+ ) {
239
+ throw new Error(
240
+ "either --committed-grant or --committed-grant-file (grant CBOR, with --entry-id) is required",
241
+ );
242
+ }
243
+ if (
244
+ options.committedGrantFile !== undefined &&
245
+ options.entryId === undefined
246
+ ) {
247
+ throw new Error("--committed-grant-file requires --entry-id");
248
+ }
249
+ return options;
250
+ }