@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.
- package/LICENSE +21 -0
- package/README.md +425 -0
- package/TRUST-MODEL.md +8 -0
- package/dist/cli.js +33811 -0
- package/dist/decode-receipt.d.ts +19 -0
- package/dist/decode-receipt.d.ts.map +1 -0
- package/dist/decode-receipt.js +19 -0
- package/dist/decode-receipt.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/decode-receipt-cbor.d.ts +26 -0
- package/dist/lib/decode-receipt-cbor.d.ts.map +1 -0
- package/dist/lib/decode-receipt-cbor.js +182 -0
- package/dist/lib/decode-receipt-cbor.js.map +1 -0
- package/dist/lib/decode-receipt-decode.d.ts +103 -0
- package/dist/lib/decode-receipt-decode.d.ts.map +1 -0
- package/dist/lib/decode-receipt-decode.js +237 -0
- package/dist/lib/decode-receipt-decode.js.map +1 -0
- package/dist/lib/decode-receipt-labels.d.ts +54 -0
- package/dist/lib/decode-receipt-labels.d.ts.map +1 -0
- package/dist/lib/decode-receipt-labels.js +117 -0
- package/dist/lib/decode-receipt-labels.js.map +1 -0
- package/dist/lib/decode-receipt-render.d.ts +11 -0
- package/dist/lib/decode-receipt-render.d.ts.map +1 -0
- package/dist/lib/decode-receipt-render.js +242 -0
- package/dist/lib/decode-receipt-render.js.map +1 -0
- package/package.json +83 -0
- package/src/cli.ts +5 -0
- package/src/command.ts +51 -0
- package/src/commands/admin-onboard-token.ts +51 -0
- package/src/commands/admin.ts +20 -0
- package/src/commands/complete-grant.ts +41 -0
- package/src/commands/create-consistency-proof.ts +44 -0
- package/src/commands/create-log.ts +95 -0
- package/src/commands/decode-receipt.ts +20 -0
- package/src/commands/delegate.ts +55 -0
- package/src/commands/deploy.ts +72 -0
- package/src/commands/fetch-accumulator.ts +49 -0
- package/src/commands/onboard-genesis.ts +70 -0
- package/src/commands/onboard-request.ts +59 -0
- package/src/commands/register-grant.ts +74 -0
- package/src/commands/register.ts +54 -0
- package/src/commands/resolve-receipt.ts +101 -0
- package/src/commands/sign-statement.ts +53 -0
- package/src/commands/verify-grant.ts +89 -0
- package/src/commands/verify.ts +92 -0
- package/src/commoncli.ts +34 -0
- package/src/decode-receipt.ts +56 -0
- package/src/index.ts +10 -0
- package/src/lib/bytes.ts +7 -0
- package/src/lib/checkpoint-provider.ts +297 -0
- package/src/lib/complete-grant-derive.ts +237 -0
- package/src/lib/consistency-proof.ts +271 -0
- package/src/lib/create-log-build.ts +168 -0
- package/src/lib/create-log-prepare.ts +75 -0
- package/src/lib/create-receipt-chain-verify.ts +267 -0
- package/src/lib/create-receipt-derive.ts +208 -0
- package/src/lib/create-receipt-inputs.ts +79 -0
- package/src/lib/decode-checkpoint-calldata.ts +188 -0
- package/src/lib/decode-receipt-cbor.ts +202 -0
- package/src/lib/decode-receipt-decode.ts +333 -0
- package/src/lib/decode-receipt-labels.ts +132 -0
- package/src/lib/decode-receipt-render.ts +284 -0
- package/src/lib/delegate-flow.ts +289 -0
- package/src/lib/delegate-voucher.ts +122 -0
- package/src/lib/deploy-artifact.ts +79 -0
- package/src/lib/deploy-bootstrap.ts +100 -0
- package/src/lib/deploy-flow.ts +184 -0
- package/src/lib/deploy-genesis-log-id.ts +29 -0
- package/src/lib/deploy-rpc.ts +40 -0
- package/src/lib/deployment-record.ts +65 -0
- package/src/lib/fsio.ts +91 -0
- package/src/lib/grant-flags-local.ts +19 -0
- package/src/lib/onboard-attestation.ts +120 -0
- package/src/lib/onboard-genesis-body.ts +94 -0
- package/src/lib/openssl-error-queue.ts +56 -0
- package/src/lib/register-flow.ts +292 -0
- package/src/lib/register-grant-build.ts +173 -0
- package/src/lib/register-grant-complete.ts +47 -0
- package/src/lib/register-grant-flow.ts +258 -0
- package/src/lib/resolve-receipt-freshen.ts +221 -0
- package/src/lib/sign-statement-build.ts +117 -0
- package/src/lib/sign-statement-key.ts +145 -0
- package/src/lib/verify-anchored.ts +326 -0
- package/src/lib/verify-checkpoint-chain.ts +160 -0
- package/src/lib/verify-eventscan.ts +214 -0
- package/src/lib/verify-inputs.ts +143 -0
- package/src/lib/verify-known-accumulator.ts +161 -0
- package/src/lib/verify-report.ts +265 -0
- package/src/lib/x402-payment.ts +115 -0
- package/src/main/admin-onboard-token.ts +216 -0
- package/src/main/complete-grant.ts +265 -0
- package/src/main/create-consistency-proof.ts +83 -0
- package/src/main/create-log.ts +347 -0
- package/src/main/create-receipt.ts +763 -0
- package/src/main/decode-receipt.ts +72 -0
- package/src/main/delegate.ts +141 -0
- package/src/main/deploy.ts +273 -0
- package/src/main/fetch-accumulator.ts +181 -0
- package/src/main/not-implemented.ts +43 -0
- package/src/main/onboard-genesis.ts +192 -0
- package/src/main/onboard-request.ts +333 -0
- package/src/main/register-grant.ts +270 -0
- package/src/main/register.ts +243 -0
- package/src/main/sign-statement.ts +168 -0
- package/src/main/verify.ts +767 -0
- package/src/options/admin-onboard-token.ts +48 -0
- package/src/options/common.ts +69 -0
- package/src/options/complete-grant.ts +40 -0
- package/src/options/create-consistency-proof.ts +63 -0
- package/src/options/create-log.ts +99 -0
- package/src/options/create-receipt.ts +243 -0
- package/src/options/decode-receipt.ts +25 -0
- package/src/options/delegate.ts +93 -0
- package/src/options/deploy.ts +105 -0
- package/src/options/fetch-accumulator.ts +62 -0
- package/src/options/onboard-genesis.ts +74 -0
- package/src/options/onboard-request.ts +57 -0
- package/src/options/register-grant.ts +72 -0
- package/src/options/register.ts +71 -0
- package/src/options/sign-statement.ts +64 -0
- package/src/options/verify.ts +250 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Robin Bryce
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
# forestrie-cli
|
|
2
|
+
|
|
3
|
+
`forestrie` — the **participant CLI** for forestrie transparency logs
|
|
4
|
+
(SCITT / COSE receipts). Ships two ways — an npm package that runs on Node
|
|
5
|
+
(`npx @forestrie/forestrie-cli`) and a dependency-free static binary on
|
|
6
|
+
every GitHub release — both hosting the subcommands a participant needs to
|
|
7
|
+
deploy, sign, register, and verify against a forestrie log:
|
|
8
|
+
|
|
9
|
+
| Subcommand | What it does |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `deploy` | Deploy a univocity instance (ES256 bootstrap is the paved path) |
|
|
12
|
+
| `sign-statement` | Produce a SCITT signed statement (plain COSE Sign1 with CWT claims) |
|
|
13
|
+
| `register` | Register a signed statement via SCRAPI, download the receipt |
|
|
14
|
+
| `register-grant` | Authorize a signer for a child/data log (one grant per signer) |
|
|
15
|
+
| `complete-grant` | Self-create the `Forestrie-Grant` header from a checkpoint |
|
|
16
|
+
| `resolve-receipt` | Produce or freshen a COSE receipt — from tiles, or tile-free from a `.sth`/calldata chain (alias: `create-receipt`) |
|
|
17
|
+
| `decode-receipt` | Decode a COSE receipt — it is just COSE: Sign1 + MMR inclusion |
|
|
18
|
+
| `verify` | Verify a receipt offline — the same closer for every other subcommand |
|
|
19
|
+
| `fetch-accumulator` | Cache the on-chain accumulator as a `--known-accumulator` snapshot |
|
|
20
|
+
|
|
21
|
+
**Status:** subcommands not yet implemented declare their real argument
|
|
22
|
+
surface and parse it, then exit non-zero with a structured `not_implemented`
|
|
23
|
+
error. `--json` emits that report as JSON on stdout. Implementations land
|
|
24
|
+
per-subcommand.
|
|
25
|
+
|
|
26
|
+
## Install from npm
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx @forestrie/forestrie-cli --help # no install
|
|
30
|
+
npm install -g @forestrie/forestrie-cli # then: forestrie --help
|
|
31
|
+
forestrie verify --help
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Runtime:** the npm package is a **Node** program — Node **>= 20.10** and
|
|
35
|
+
nothing else. Bun is the development and static-binary toolchain, not a
|
|
36
|
+
runtime requirement: nothing under `src/` uses a Bun-only API, and
|
|
37
|
+
`dist/cli.js` is built with `--target node`. If you would rather have no
|
|
38
|
+
runtime at all, take the static binary below.
|
|
39
|
+
|
|
40
|
+
## Install from a release
|
|
41
|
+
|
|
42
|
+
Every [GitHub release](https://github.com/forestrie/forestrie-cli/releases)
|
|
43
|
+
attaches prebuilt static binaries — `forestrie-darwin-arm64` and
|
|
44
|
+
`forestrie-linux-x64` — each with a `.sha256` sidecar. No runtime is
|
|
45
|
+
needed; just download, verify, and run:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
target=darwin-arm64 # or: linux-x64
|
|
49
|
+
base=https://github.com/forestrie/forestrie-cli/releases/latest/download
|
|
50
|
+
curl -fsSLO "${base}/forestrie-${target}"
|
|
51
|
+
curl -fsSLO "${base}/forestrie-${target}.sha256"
|
|
52
|
+
shasum -a 256 -c "forestrie-${target}.sha256"
|
|
53
|
+
chmod +x "forestrie-${target}"
|
|
54
|
+
./forestrie-${target} --help
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Releases are cut by pushing a `v*` tag; the workflow fails closed unless
|
|
58
|
+
the tag matches the `package.json` version
|
|
59
|
+
([release.yml](https://github.com/forestrie/forestrie-cli/blob/main/.github/workflows/release.yml)).
|
|
60
|
+
The same tag publishes the npm package
|
|
61
|
+
([publish.yml](https://github.com/forestrie/forestrie-cli/blob/main/.github/workflows/publish.yml)),
|
|
62
|
+
with npm trusted publishing and provenance attestation.
|
|
63
|
+
|
|
64
|
+
## Install from source
|
|
65
|
+
|
|
66
|
+
Requires [Bun](https://bun.sh) (pinned in `mise.toml`; `mise install`
|
|
67
|
+
works). All dependencies install **tokenless** from public npmjs — no
|
|
68
|
+
registry auth, no `.npmrc`:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
bun install
|
|
72
|
+
bun run typecheck && bun test
|
|
73
|
+
bun run dev -- --help # run from source
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Build the static binary
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
bun run build:binary # host platform → dist/forestrie
|
|
80
|
+
bun run build:binary:linux-x64 # → dist/forestrie-linux-x64
|
|
81
|
+
bun run build:binary:darwin-arm64
|
|
82
|
+
./dist/forestrie --help
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Conventions
|
|
86
|
+
|
|
87
|
+
- Human output by default; `--json` for machine-readable output on stdout.
|
|
88
|
+
- `-v` / `--verbosity` per `@forestrie/cli-kit` reporting (`-1` silences
|
|
89
|
+
stderr feedback; stdout stays pipeable).
|
|
90
|
+
- Env fallbacks: `FORESTRIE_BASE_URL`, `RPC_URL`, `GRANT_B64`,
|
|
91
|
+
`OWNER_ADDRESS`, `DEPLOYER_KEY`, `FORESTRIE_CONFIG`; any string flag also
|
|
92
|
+
accepts `${env:VAR}` / `${env}` whole-value templates.
|
|
93
|
+
- **ES256 is the paved path end-to-end**; KS256 appears only in the
|
|
94
|
+
Safe / ERC-1271 aside.
|
|
95
|
+
|
|
96
|
+
## Usage
|
|
97
|
+
|
|
98
|
+
Minimal example per subcommand. `$FORESTRIE_BASE_URL`, `$RPC_URL`, and
|
|
99
|
+
`$GRANT_B64` are the env fallbacks from [Conventions](#conventions).
|
|
100
|
+
|
|
101
|
+
### `deploy`
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
forestrie deploy \
|
|
105
|
+
--bootstrap-alg es256 \
|
|
106
|
+
--bootstrap-es256-generate --bootstrap-es256-pem-out bootstrap.es256.pem \
|
|
107
|
+
--owner-address 0xYourDeployer \
|
|
108
|
+
--rpc-url "$RPC_URL" \
|
|
109
|
+
--out deployment.json
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
ImutableUnivocity deployed at: 0xAbC…123
|
|
114
|
+
genesisLogId: 0f9a1c7e-…-…
|
|
115
|
+
chainId: 84532 txHash: 0x9f…21
|
|
116
|
+
wrote deployment record to deployment.json
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### `sign-statement`
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
forestrie sign-statement \
|
|
123
|
+
--key alice.es256.pem \
|
|
124
|
+
--payload statement.json --content-type application/json \
|
|
125
|
+
--out statement.cose
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
signed statement: plain COSE Sign1 (ES256)
|
|
130
|
+
kid: 241115ab754013fcbf2e88544a369009d5d7de7f54497ad640ef28ad6237392c
|
|
131
|
+
iss: 241115ab754013fcbf2e88544a369009d5d7de7f54497ad640ef28ad6237392c
|
|
132
|
+
sub: sha-256:9c3f8a5d0e6b17c4a2f1d8e05b6c93a7714f2e8d90ab35c6e1d47f80b92c5a13
|
|
133
|
+
payload: 45 bytes (application/json)
|
|
134
|
+
statement: 253 bytes -> statement.cose
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Statements carry SCITT CWT claims (protected COSE header label 15,
|
|
138
|
+
covered by the signature). Defaults keep signing zero-config and
|
|
139
|
+
deterministic; every claim is settable:
|
|
140
|
+
|
|
141
|
+
- `--iss <string-or-uri>` — issuer (CWT claim 1). Default: the
|
|
142
|
+
lowercase hex kid. The keyword `ckt` derives the RFC 9679 COSE Key
|
|
143
|
+
Thumbprint URI (`urn:ietf:params:oauth:ckt:sha-256:…`) from the
|
|
144
|
+
signing key — which also means a literal issuer named `ckt` is not
|
|
145
|
+
expressible.
|
|
146
|
+
- `--sub <string-or-uri>` — subject the statement speaks about (CWT
|
|
147
|
+
claim 2). Default: `sha-256:<hex>` of the payload bytes.
|
|
148
|
+
- `--iat now|<unix-seconds>` — issued-at (CWT claim 6). Omitted by
|
|
149
|
+
default so repeated signing of the same payload is byte-identical.
|
|
150
|
+
|
|
151
|
+
Empty `--iss`/`--sub` values are rejected rather than silently
|
|
152
|
+
replaced with the defaults.
|
|
153
|
+
|
|
154
|
+
### `register`
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
forestrie register \
|
|
158
|
+
--base-url "$FORESTRIE_BASE_URL" \
|
|
159
|
+
--log-id "$LOG_ID" \
|
|
160
|
+
--statement statement.cose \
|
|
161
|
+
--grant-b64 "$GRANT_B64" \
|
|
162
|
+
--out receipt.cbor
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
entryId: 0202020202020202…0001
|
|
167
|
+
statusUrl: https://.../register/.../entries/…
|
|
168
|
+
receiptUrl: …/receipt
|
|
169
|
+
wrote receipt (612 bytes) to receipt.cbor
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### `register-grant`
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
forestrie register-grant \
|
|
176
|
+
--base-url "$FORESTRIE_BASE_URL" \
|
|
177
|
+
--owner-log "$OWNER_LOG_ID" --data-log "$DATA_LOG_ID" \
|
|
178
|
+
--sign-with bootstrap.es256.pem \
|
|
179
|
+
--out-b64 grant.b64
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
ownerLog: 0f9a1c7e-…-… (grant leaf)
|
|
184
|
+
dataLog: 8c2e4b…-… (authorized)
|
|
185
|
+
signer: 04a91f…
|
|
186
|
+
entryId: …
|
|
187
|
+
wrote completed grant base64 to grant.b64
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### `complete-grant`
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
forestrie complete-grant \
|
|
194
|
+
--grant grant.b64 \
|
|
195
|
+
--checkpoint checkpoint.sth --massif massif.log \
|
|
196
|
+
--out-b64 grant.completed.b64
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
complete-grant: leaf — mmrIndex 0 (recovered from massif)
|
|
201
|
+
complete-grant: entry id — 01010101010101010000000000000000 (idtimestamp from massif)
|
|
202
|
+
complete-grant: proof — 1 node(s) to peak 1/2
|
|
203
|
+
complete-grant: checkpoint — sealed size 4, delegation cert copied: no
|
|
204
|
+
complete-grant: receipt — 118 bytes attached (header 396)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Self-creates the `Forestrie-Grant` header from a checkpoint, without an
|
|
208
|
+
operator round-trip: it locates the grant's leaf in the local `--massif` blob
|
|
209
|
+
by its commitment hash (recovering the `mmrIndex` and the sequenced
|
|
210
|
+
idtimestamp), rebuilds the inclusion receipt against the checkpoint's pre-signed
|
|
211
|
+
peak, and attaches the receipt + idtimestamp — the same completed bearer
|
|
212
|
+
`register-grant` produces online. `--massif` is required (it carries the leaf);
|
|
213
|
+
`--idtimestamp <hex|path>` is a fallback for the rare massif with no index
|
|
214
|
+
region.
|
|
215
|
+
|
|
216
|
+
### `resolve-receipt`
|
|
217
|
+
|
|
218
|
+
One receipt producer (SCRAPI §2.4 "Resolve Receipt"; `create-receipt` is a
|
|
219
|
+
kept alias). The **source is chosen by the flags present**, not a `--mode`;
|
|
220
|
+
passing more than one source, or an under-specified one, errors with guidance.
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
# from tiles: rebuild the leaf→peak path from a massif and a checkpoint
|
|
224
|
+
forestrie resolve-receipt \
|
|
225
|
+
--massif massif.log --checkpoint checkpoint.sth \
|
|
226
|
+
--mmr-index 0 \
|
|
227
|
+
--out receipt.cbor
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
resolve-receipt: massif — index 0 (height 3, mmr indexes 0..3)
|
|
232
|
+
resolve-receipt: leaf — mmrIndex 0 (from --mmr-index)
|
|
233
|
+
resolve-receipt: checkpoint — sealed size 4, 2 peak(s)
|
|
234
|
+
resolve-receipt: proof — 1 node(s) to peak 1/2 (mmrIndex 2)
|
|
235
|
+
resolve-receipt: receipt — 144 bytes -> receipt.cbor
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Add `--univocity <address> --log-id <id> --rpc-url $RPC_URL` for a
|
|
239
|
+
chain-anchored (report-only) verification instead of a checkpoint-based
|
|
240
|
+
receipt.
|
|
241
|
+
|
|
242
|
+
#### Freshen a stale receipt (tile-free)
|
|
243
|
+
|
|
244
|
+
When log growth buries the peak a receipt commits to, **freshen** re-anchors
|
|
245
|
+
it to the current sealed state without tiles. `resolve-receipt --receipt
|
|
246
|
+
<stale>` plus a tile-free source extends the receipt's old inclusion path to
|
|
247
|
+
the latest peak and re-emits it. The leaf value is recomputed exactly as
|
|
248
|
+
`verify` does, from the same leaf inputs: `--payload <statement>` for statement
|
|
249
|
+
receipts, or `--committed-grant`/`--committed-grant-file` for grant receipts
|
|
250
|
+
(both with `--entry-id`).
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# a STATEMENT receipt from a retained .sth chain (emitted under the chain's tail)
|
|
254
|
+
forestrie resolve-receipt \
|
|
255
|
+
--receipt stale.cbor --checkpoint-chain ./checkpoints/ \
|
|
256
|
+
--payload statement.cose --entry-id <hex> \
|
|
257
|
+
--in-place
|
|
258
|
+
|
|
259
|
+
# a GRANT receipt, same chain source
|
|
260
|
+
forestrie resolve-receipt \
|
|
261
|
+
--receipt stale.cbor --checkpoint-chain ./checkpoints/ \
|
|
262
|
+
--committed-grant-file grant.cbor --entry-id <hex> \
|
|
263
|
+
--in-place
|
|
264
|
+
|
|
265
|
+
# from on-chain publishCheckpoint calldata; calldata carries no peak receipts,
|
|
266
|
+
# so the latest .sth is supplied for emission, and --known-accumulator binds
|
|
267
|
+
# the folded state to a trusted snapshot
|
|
268
|
+
forestrie resolve-receipt \
|
|
269
|
+
--receipt stale.cbor \
|
|
270
|
+
--rpc-url $RPC_URL --univocity <address> --log-id <id> \
|
|
271
|
+
--checkpoint latest.sth --known-accumulator accumulator.cbor \
|
|
272
|
+
--committed-grant-file grant.cbor --entry-id <hex> \
|
|
273
|
+
--out fresh.cbor
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
`--in-place` rewrites the `--receipt` file (mutually exclusive with `--out`;
|
|
277
|
+
crash-safe — written to a sibling temp then atomically renamed). The freshened
|
|
278
|
+
receipt is a native receipt that verifies with plain `verify` against the
|
|
279
|
+
current state, and freshen fails closed (it never emits a receipt whose
|
|
280
|
+
recomputed peak does not match the folded latest accumulator).
|
|
281
|
+
|
|
282
|
+
#### Bind the freshened state (`--known-accumulator`)
|
|
283
|
+
|
|
284
|
+
Both freshen sources fold a consistency-proof chain to the current accumulator;
|
|
285
|
+
they differ only in the **emission checkpoint**. `--checkpoint-chain` emits under
|
|
286
|
+
the chain's own tail (self-contained — reach for it by default). The calldata
|
|
287
|
+
source borrows a **separately-supplied** latest `--checkpoint`, so pass
|
|
288
|
+
**`--known-accumulator <snapshot>`** (a `fetch-accumulator` capture) to bind the
|
|
289
|
+
folded state to a trusted chain read: freshen asserts it **equals your snapshot
|
|
290
|
+
at the same size** and fails closed otherwise (this also catches a lying/stale
|
|
291
|
+
RPC). `--known-accumulator` works with either source.
|
|
292
|
+
|
|
293
|
+
`--known-accumulator` anchors **freshness** — that the leaf roots into the
|
|
294
|
+
genuine current log — which is a *different* question from who sealed the state
|
|
295
|
+
or whether the log's authority chains back to genesis. Freshen never re-opens
|
|
296
|
+
those; it re-anchors an already-issued receipt to the current accumulator. Which
|
|
297
|
+
anchor proves what is the trust model: see
|
|
298
|
+
**[the trust model](https://github.com/forestrie/protocol/blob/main/spec/receipt-trust-model.md#freshen-and-the-attestor)**.
|
|
299
|
+
|
|
300
|
+
### `decode-receipt`
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
forestrie decode-receipt receipt.cbor
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
```
|
|
307
|
+
COSE_Sign1 — tagged 18 (COSE_Sign1) — 304 bytes
|
|
308
|
+
├─ protected: 1 (alg): -7 — ES256 4 (kid): 6c6c6c6c…
|
|
309
|
+
├─ unprotected: inclusion proof — mmr index 5, path length 3
|
|
310
|
+
├─ payload: detached — recomputed from the inclusion path at verify time
|
|
311
|
+
└─ signature: 64 bytes
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### `verify`
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
forestrie verify \
|
|
318
|
+
--genesis genesis.cbor --receipt receipt.cbor --committed-grant "$GRANT_B64"
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
verify: parse ok — receipt COSE decodes; genesis trust root loads (ES256)
|
|
323
|
+
verify: signature ok — checkpoint signature verifies under the genesis trust key
|
|
324
|
+
verify: inclusion ok — proof path recomputes the checkpoint peak
|
|
325
|
+
verify: binding ok — leaf binds the grant commitment at the receipt idtimestamp
|
|
326
|
+
PASS: receipt verified offline against the cached checkpoint
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Add `--univocity <address> --log-id <id> --rpc-url $RPC_URL` to check the
|
|
330
|
+
receipt's peak against the on-chain accumulator instead of the checkpoint
|
|
331
|
+
signature — no operator trust required.
|
|
332
|
+
|
|
333
|
+
#### Trust anchors (FOR-297)
|
|
334
|
+
|
|
335
|
+
`verify` takes four named trust anchors — pass whichever you hold:
|
|
336
|
+
|
|
337
|
+
- **`--known-log-key`** — a caller-known log owner key (base64 `x||y`, env
|
|
338
|
+
`KNOWN_LOG_KEY`); fully offline, checks the signature under a key you trust
|
|
339
|
+
out of band (the SCITT / SSH-known-hosts posture).
|
|
340
|
+
- **`--genesis`** — a cached `genesis.cbor` (the univocity-instance root
|
|
341
|
+
registration). Roots a receipt whose signer chains to that root owner; for a
|
|
342
|
+
child log deeper in the hierarchy, reaching genesis is the grant-chain walk
|
|
343
|
+
(still open) or the on-chain path below.
|
|
344
|
+
- **`--known-accumulator`** — a cached chain read (`forestrie fetch-accumulator`):
|
|
345
|
+
verifies the peak against the on-chain accumulator, no operator trust and no
|
|
346
|
+
signature needed. Never source it unauthenticated from the operator's tile
|
|
347
|
+
store. Older receipts extend to a newer snapshot via `--massif`.
|
|
348
|
+
- **`--rpc-url`** — a live chain read; same as `--known-accumulator`, current.
|
|
349
|
+
|
|
350
|
+
What each actually proves — and how forestrie separates **split-view** (is this
|
|
351
|
+
a single, un-forked history) from **sealing** (who signed this state) from
|
|
352
|
+
**authority** (does the log chain to genesis, via grants — not `genesis.cbor`)
|
|
353
|
+
from **attribution** (who was authorised to sign this leaf) — is the trust
|
|
354
|
+
model: see
|
|
355
|
+
**[the trust model](https://github.com/forestrie/protocol/blob/main/spec/receipt-trust-model.md)**.
|
|
356
|
+
The receipt never expires, and the anchor never needs to be current — only
|
|
357
|
+
trusted.
|
|
358
|
+
|
|
359
|
+
Note that **freshness is not one of the four**. It is a weaker axis that bears
|
|
360
|
+
only on *coverage* — whether a given accumulator snapshot reaches your leaf —
|
|
361
|
+
and collapsing it into split-view is the specific confusion the trust model
|
|
362
|
+
warns against. Split-view is the load-bearing property; currency is at most the
|
|
363
|
+
`--rpc-url` "as of now" delta.
|
|
364
|
+
|
|
365
|
+
### `fetch-accumulator`
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
forestrie fetch-accumulator \
|
|
369
|
+
--univocity "$UNIVOCITY" --log-id "$LOG_ID" --rpc-url "$RPC_URL" \
|
|
370
|
+
--out accumulator.cbor
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Reads `logState(logId)` pinned to a block and writes the
|
|
374
|
+
`--known-accumulator` snapshot: canonical CBOR binding
|
|
375
|
+
`(chainId, univocity, logId, size, blockNumber, blockHash)` — anyone with
|
|
376
|
+
RPC can re-run the read at that block and confirm or disprove it.
|
|
377
|
+
|
|
378
|
+
## Use as a library: `decode-receipt`
|
|
379
|
+
|
|
380
|
+
The receipt decoder behind `forestrie decode-receipt` is exported as a
|
|
381
|
+
subpath so other tools can render receipts without shelling out to the CLI
|
|
382
|
+
or vendoring a copy:
|
|
383
|
+
|
|
384
|
+
```ts
|
|
385
|
+
import {
|
|
386
|
+
decodeReceipt,
|
|
387
|
+
renderReceipt,
|
|
388
|
+
HEADER_LABELS,
|
|
389
|
+
} from "@forestrie/forestrie-cli/decode-receipt";
|
|
390
|
+
|
|
391
|
+
const decoded = decodeReceipt(receiptBytes); // Uint8Array in
|
|
392
|
+
console.log(renderReceipt(decoded)); // the annotated tree
|
|
393
|
+
console.log(JSON.stringify(decoded, null, 2)); // the same model as --json
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
The entry point is pure: no `node:*`, no `Bun.*`, no I/O and no network, so
|
|
397
|
+
it runs unchanged in Node, Bun, Deno, a worker or a browser. Its only
|
|
398
|
+
dependencies are `@forestrie/receipt-verify` (the load-bearing receipt
|
|
399
|
+
parse) and `@forestrie/encoding`. Ships with `.d.ts`.
|
|
400
|
+
|
|
401
|
+
Also exported: `DecodeReceiptError` (carries the failing `stage`), the
|
|
402
|
+
display-model types (`DecodedReceipt`, `DecodedHeaderEntry`, `DecodedClaim`,
|
|
403
|
+
`Json`), the label tables (`HEADER_LABELS`, `ALG_NAMES`, `VDS_NAMES`,
|
|
404
|
+
`CWT_CLAIM_NAMES`, `COSE_KEY_PARAM_NAMES`, `PROOF_KIND_NAMES`,
|
|
405
|
+
`headerLabelInfo`) and the minimal CBOR reader (`decodeCborValue`,
|
|
406
|
+
`decodeCborMap`, `isCborTagged`). The bare `@forestrie/forestrie-cli`
|
|
407
|
+
specifier re-exports the same surface; prefer the subpath.
|
|
408
|
+
|
|
409
|
+
Decoding is **display only** — it does not check signatures or inclusion.
|
|
410
|
+
That is `forestrie verify` (and `@forestrie/receipt-verify` in-process).
|
|
411
|
+
|
|
412
|
+
## Built on (published packages)
|
|
413
|
+
|
|
414
|
+
The binary is buildable from **published packages only** (plan-2607-12) —
|
|
415
|
+
all public on npmjs under the `@forestrie` scope, MIT licensed per-package,
|
|
416
|
+
installable without auth:
|
|
417
|
+
|
|
418
|
+
`@forestrie/cli-kit` · `@forestrie/deploy-core` · `@forestrie/encoding` ·
|
|
419
|
+
`@forestrie/grant-builder` · `@forestrie/scrapi-client` ·
|
|
420
|
+
`@forestrie/receipt-verify` · `@forestrie/merklelog` ·
|
|
421
|
+
`@forestrie/delegation-cose` · `viem` (deploy tx signing/submission)
|
|
422
|
+
|
|
423
|
+
## License
|
|
424
|
+
|
|
425
|
+
MIT — see [LICENSE](./LICENSE).
|
package/TRUST-MODEL.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Forestrie receipt trust model — moved
|
|
2
|
+
|
|
3
|
+
Moved to `forestrie/protocol`:
|
|
4
|
+
**[`spec/receipt-trust-model.md`](https://github.com/forestrie/protocol/blob/main/spec/receipt-trust-model.md)**
|
|
5
|
+
(anchors preserved, including
|
|
6
|
+
[`#freshen-and-the-attestor`](https://github.com/forestrie/protocol/blob/main/spec/receipt-trust-model.md#freshen-and-the-attestor)).
|
|
7
|
+
|
|
8
|
+
Per-command recipes remain in [README.md](./README.md).
|