@forestrie/forestrie-cli 0.9.1 → 0.10.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/dist/cli.js +1084 -193
- package/dist/lib/decode-receipt-labels.d.ts +7 -6
- package/dist/lib/decode-receipt-labels.d.ts.map +1 -1
- package/dist/lib/decode-receipt-labels.js +11 -10
- package/dist/lib/decode-receipt-labels.js.map +1 -1
- package/package.json +4 -4
- package/src/commands/verify-grant.ts +1 -1
- package/src/commands/verify.ts +1 -1
- package/src/lib/checkpoint-provider.ts +73 -30
- package/src/lib/complete-grant-derive.ts +1 -0
- package/src/lib/create-log-prepare.ts +3 -1
- package/src/lib/create-receipt-derive.ts +16 -0
- package/src/lib/decode-receipt-labels.ts +11 -10
- package/src/lib/delegate-flow.ts +4 -2
- package/src/lib/http-problem.ts +53 -0
- package/src/lib/resolve-receipt-freshen.ts +9 -11
- package/src/lib/verify-anchored.ts +1 -1
- package/src/lib/verify-checkpoint-chain.ts +2 -2
- package/src/lib/verify-eventscan.ts +2 -2
- package/src/main/onboard-genesis.ts +3 -1
- package/src/main/onboard-request.ts +4 -2
- package/src/options/verify.ts +24 -18
|
@@ -46,12 +46,13 @@ export declare const HEADER_LABELS: ReadonlyMap<number, LabelInfo>;
|
|
|
46
46
|
/** COSE algorithm names (RFC 9053 + forestrie private-use). */
|
|
47
47
|
export declare const ALG_NAMES: ReadonlyMap<number, string>;
|
|
48
48
|
/**
|
|
49
|
-
* Verifiable data structure ids (
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* never as registry fact
|
|
49
|
+
* Verifiable data structure ids (the COSE receipts registry, RFC 9942).
|
|
50
|
+
* 3 is NOT a registered codepoint: draft-bryce-cose-receipts-mmr-profile
|
|
51
|
+
* requests TBD_1 (registration pending). The Go sealer emits `395: 3` on
|
|
52
|
+
* every checkpoint and peak receipt; the TypeScript exporters do not, so a
|
|
53
|
+
* verifier must not require it. Render it as the draft's unregistered
|
|
54
|
+
* codepoint, never as registry fact. The display strings are the protocol
|
|
55
|
+
* registry's (spec/label-registry.md).
|
|
55
56
|
*/
|
|
56
57
|
export declare const VDS_NAMES: ReadonlyMap<number, string>;
|
|
57
58
|
/** CWT claim keys (RFC 8392 §3.1, cnf per RFC 8747). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decode-receipt-labels.d.ts","sourceRoot":"","sources":["../../src/lib/decode-receipt-labels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,6CAA6C;AAC7C,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,iFAAiF;AACjF,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,uFAAuF;AACvF,eAAO,MAAM,SAAS,MAAM,CAAC;AAC7B,8CAA8C;AAC9C,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAC1C,oFAAoF;AACpF,eAAO,MAAM,wBAAwB,SAAS,CAAC;AAC/C;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,SAAS,CAAC;AAExC,8CAA8C;AAC9C,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,gDAAgD;AAChD,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAuEvD,CAAC;AAEH,+DAA+D;AAC/D,eAAO,MAAM,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAWhD,CAAC;AAEH
|
|
1
|
+
{"version":3,"file":"decode-receipt-labels.d.ts","sourceRoot":"","sources":["../../src/lib/decode-receipt-labels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,6CAA6C;AAC7C,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,iFAAiF;AACjF,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,uFAAuF;AACvF,eAAO,MAAM,SAAS,MAAM,CAAC;AAC7B,8CAA8C;AAC9C,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAC1C,oFAAoF;AACpF,eAAO,MAAM,wBAAwB,SAAS,CAAC;AAC/C;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,SAAS,CAAC;AAExC,8CAA8C;AAC9C,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,gDAAgD;AAChD,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAuEvD,CAAC;AAEH,+DAA+D;AAC/D,eAAO,MAAM,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAWhD,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAIhD,CAAC;AAEH,wDAAwD;AACxD,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAStD,CAAC;AAEH,yEAAyE;AACzE,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAO3D,CAAC;AAEH,mEAAmE;AACnE,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAGvD,CAAC;AAEH,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAE/D"}
|
|
@@ -47,11 +47,11 @@ export const HEADER_LABELS = new Map([
|
|
|
47
47
|
[CWT_CLAIMS_LABEL, { name: "CWT claims", note: "RFC 9597" }],
|
|
48
48
|
[
|
|
49
49
|
VDS_LABEL,
|
|
50
|
-
{ name: "verifiable data structure", note: "COSE receipts (
|
|
50
|
+
{ name: "verifiable data structure", note: "COSE receipts (RFC 9942)" },
|
|
51
51
|
],
|
|
52
52
|
[
|
|
53
53
|
VERIFIABLE_PROOFS_LABEL,
|
|
54
|
-
{ name: "verifiable proofs", note: "COSE receipts (
|
|
54
|
+
{ name: "verifiable proofs", note: "COSE receipts (RFC 9942)" },
|
|
55
55
|
],
|
|
56
56
|
[
|
|
57
57
|
DELEGATION_CERT_LABEL,
|
|
@@ -115,17 +115,18 @@ export const ALG_NAMES = new Map([
|
|
|
115
115
|
[-65799, "KS256 (secp256k1 + Keccak-256, forestrie private-use)"],
|
|
116
116
|
[
|
|
117
117
|
-65800,
|
|
118
|
-
"ES256-WebAuthn (forestrie private-use; delegation proofs
|
|
119
|
-
"
|
|
118
|
+
"ES256-WebAuthn (forestrie private-use; delegation proofs, certificates " +
|
|
119
|
+
"and session-key endorsements, never checkpoint-signing)",
|
|
120
120
|
],
|
|
121
121
|
]);
|
|
122
122
|
/**
|
|
123
|
-
* Verifiable data structure ids (
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* never as registry fact
|
|
123
|
+
* Verifiable data structure ids (the COSE receipts registry, RFC 9942).
|
|
124
|
+
* 3 is NOT a registered codepoint: draft-bryce-cose-receipts-mmr-profile
|
|
125
|
+
* requests TBD_1 (registration pending). The Go sealer emits `395: 3` on
|
|
126
|
+
* every checkpoint and peak receipt; the TypeScript exporters do not, so a
|
|
127
|
+
* verifier must not require it. Render it as the draft's unregistered
|
|
128
|
+
* codepoint, never as registry fact. The display strings are the protocol
|
|
129
|
+
* registry's (spec/label-registry.md).
|
|
129
130
|
*/
|
|
130
131
|
export const VDS_NAMES = new Map([
|
|
131
132
|
[1, "RFC9162_SHA256 (Certificate Transparency)"],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decode-receipt-labels.js","sourceRoot":"","sources":["../../src/lib/decode-receipt-labels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,6CAA6C;AAC7C,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AAEjC,iFAAiF;AACjF,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,CAAC;AAC3C,uFAAuF;AACvF,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC;AAC7B,8CAA8C;AAC9C,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AACnC,gFAAgF;AAChF,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAC1C,oFAAoF;AACpF,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,KAAK,CAAC;AAC/C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAK,CAAC;AAExC,8CAA8C;AAC9C,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,CAAC;AACvC,gDAAgD;AAChD,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,CAAC;AAQzC,6EAA6E;AAC7E,MAAM,CAAC,MAAM,aAAa,GAAmC,IAAI,GAAG,CAAC;IACnE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACpB,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACrB,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;IAC7B,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACpB,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACnB,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;IAC3B,CAAC,gBAAgB,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC5D;QACE,SAAS;QACT,EAAE,IAAI,EAAE,2BAA2B,EAAE,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"decode-receipt-labels.js","sourceRoot":"","sources":["../../src/lib/decode-receipt-labels.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forestrie/forestrie-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "forestrie — participant CLI for forestrie transparency logs (SCITT / COSE receipts)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"forestrie",
|
|
@@ -68,10 +68,10 @@
|
|
|
68
68
|
"@forestrie/cli-kit": "^0.1.1",
|
|
69
69
|
"@forestrie/delegation-cose": "^0.3.2",
|
|
70
70
|
"@forestrie/deploy-core": "^0.7.1",
|
|
71
|
-
"@forestrie/encoding": "^0.
|
|
71
|
+
"@forestrie/encoding": "^0.9.1",
|
|
72
72
|
"@forestrie/grant-builder": "^0.5.0",
|
|
73
|
-
"@forestrie/merklelog": "^0.
|
|
74
|
-
"@forestrie/receipt-verify": "^
|
|
73
|
+
"@forestrie/merklelog": "^0.5.0",
|
|
74
|
+
"@forestrie/receipt-verify": "^3.0.1",
|
|
75
75
|
"@forestrie/scrapi-client": "^0.2.2",
|
|
76
76
|
"citty": "^0.2.2",
|
|
77
77
|
"viem": "^2.31.0"
|
|
@@ -18,7 +18,7 @@ export default defineForestrieCommand({
|
|
|
18
18
|
"known-log-key": {
|
|
19
19
|
type: "string",
|
|
20
20
|
description:
|
|
21
|
-
"Caller-known log OWNER key (the delegation issuer), base64 x||y (64 bytes) (env KNOWN_LOG_KEY). Offline signer-provenance anchor that replaces --genesis; asserts (does not prove) the key-to-log binding — --genesis
|
|
21
|
+
"Caller-known log OWNER key (the delegation issuer), base64 x||y (64 bytes) (env KNOWN_LOG_KEY). Offline signer-provenance anchor that replaces --genesis; asserts (does not prove) the key-to-log binding — --genesis roots it in the forest genesis document's bootstrap key (the root log, or a delegation directly under it), while the accumulator roots (--known-accumulator / --rpc-url) supply split-view (freshness is a weaker, separate axis bearing only on coverage). See https://github.com/forestrie/protocol/blob/main/spec/receipt-trust-model.md",
|
|
22
22
|
valueHint: "base64",
|
|
23
23
|
},
|
|
24
24
|
receipt: {
|
package/src/commands/verify.ts
CHANGED
|
@@ -18,7 +18,7 @@ export default defineForestrieCommand({
|
|
|
18
18
|
"known-log-key": {
|
|
19
19
|
type: "string",
|
|
20
20
|
description:
|
|
21
|
-
"Caller-known log OWNER key (the delegation issuer), base64 x||y (64 bytes) (env KNOWN_LOG_KEY). Offline signer-provenance anchor that replaces --genesis; asserts (does not prove) the key-to-log binding — --genesis
|
|
21
|
+
"Caller-known log OWNER key (the delegation issuer), base64 x||y (64 bytes) (env KNOWN_LOG_KEY). Offline signer-provenance anchor that replaces --genesis; asserts (does not prove) the key-to-log binding — --genesis roots it in the forest genesis document's bootstrap key (the root log, or a delegation directly under it), while the accumulator roots (--known-accumulator / --rpc-url) supply split-view (freshness is a weaker, separate axis bearing only on coverage). See https://github.com/forestrie/protocol/blob/main/spec/receipt-trust-model.md",
|
|
22
22
|
valueHint: "base64",
|
|
23
23
|
},
|
|
24
24
|
receipt: {
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
computeCheckpointAccumulator,
|
|
32
32
|
checkpointConsistencyProof,
|
|
33
33
|
type CheckpointConsistencyProof,
|
|
34
|
+
type DecodedConsistencyProof,
|
|
34
35
|
} from "@forestrie/receipt-verify";
|
|
35
36
|
import { fetchPublishedCheckpoints } from "./verify-eventscan.js";
|
|
36
37
|
import {
|
|
@@ -56,21 +57,38 @@ export type CheckpointSeal =
|
|
|
56
57
|
| { kind: "sth"; checkpointBytes: Uint8Array };
|
|
57
58
|
|
|
58
59
|
/**
|
|
59
|
-
*
|
|
60
|
-
* proofs (`CalldataConsistencyProof` —
|
|
61
|
-
*
|
|
62
|
-
* `CheckpointConsistencyProof` (
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
60
|
+
* One fold step: a single consistency proof, shared by calldata-decoded
|
|
61
|
+
* proofs (`CalldataConsistencyProof` — on-chain calldata carries no protected
|
|
62
|
+
* header, so no signed size) and `.sth`-decoded proofs. receipt-verify
|
|
63
|
+
* decodes an `.sth` to a `CheckpointConsistencyProof` CHAIN (`proofs[]`, one
|
|
64
|
+
* or more relayed steps under the head signature, ADR-0066 D2); this provider
|
|
65
|
+
* keeps one link per step, so a link's proof is the single-step
|
|
66
|
+
* `DecodedConsistencyProof`, and {@link singleProofChain} wraps it back into
|
|
67
|
+
* the chain shape where receipt-verify's parameter type demands it. The fold
|
|
68
|
+
* never reads `signedTreeSize2` — only the trusted base SIZE (passed
|
|
69
|
+
* separately, never off the proof) and `treeSize1`/`treeSize2`/`paths`/
|
|
70
|
+
* `rightPeaks` matter — so it is backfilled with `treeSize2`.
|
|
69
71
|
*/
|
|
70
|
-
export type FoldableConsistencyProof =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
export type FoldableConsistencyProof = DecodedConsistencyProof;
|
|
73
|
+
|
|
74
|
+
/** Wrap one fold step as a one-proof chain for receipt-verify's chain-typed
|
|
75
|
+
* parameters. `signedTreeSize2` is the checkpoint's SIGNED size when the
|
|
76
|
+
* step is the last one a sealed `.sth` relays (so `freshenReceipt`'s
|
|
77
|
+
* signed-vs-declared guard stays live for it), and is backfilled from
|
|
78
|
+
* `treeSize2` for steps that carry no signature of their own: calldata
|
|
79
|
+
* proofs, and the earlier steps a checkpoint relays (see
|
|
80
|
+
* {@link FoldableConsistencyProof}). */
|
|
81
|
+
export function singleProofChain(
|
|
82
|
+
p: FoldableConsistencyProof,
|
|
83
|
+
signedTreeSize2: bigint = p.treeSize2,
|
|
84
|
+
): CheckpointConsistencyProof {
|
|
85
|
+
return {
|
|
86
|
+
proofs: [p],
|
|
87
|
+
treeSize1: p.treeSize1,
|
|
88
|
+
treeSize2: p.treeSize2,
|
|
89
|
+
signedTreeSize2,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
74
92
|
|
|
75
93
|
/** One folded link: the accumulator committed at `treeSize2`. */
|
|
76
94
|
export type CheckpointLink = {
|
|
@@ -82,6 +100,10 @@ export type CheckpointLink = {
|
|
|
82
100
|
* (the tile-free climb material). Retained so a path-extending consumer
|
|
83
101
|
* (freshen) gets it without a re-decode; folding consumers ignore it. */
|
|
84
102
|
proof: FoldableConsistencyProof;
|
|
103
|
+
/** The SIGNED tree-size-2 covering this link, when it is the last step of a
|
|
104
|
+
* sealed `.sth` (cross-checked against `proof.treeSize2` at decode). Absent
|
|
105
|
+
* for calldata links and for the earlier steps a checkpoint relays. */
|
|
106
|
+
signedTreeSize2?: bigint;
|
|
85
107
|
/** The signature material to verify this link's accumulator, when directly
|
|
86
108
|
* signed (see {@link CheckpointSeal}). The provider does NOT verify it. */
|
|
87
109
|
seal?: CheckpointSeal;
|
|
@@ -103,7 +125,9 @@ export async function foldProofChain(
|
|
|
103
125
|
accumulatorFrom?: Uint8Array[];
|
|
104
126
|
accumulatorFromSize?: bigint;
|
|
105
127
|
seals?: readonly (CheckpointSeal | undefined)[];
|
|
106
|
-
sourceRefs?: readonly string[];
|
|
128
|
+
sourceRefs?: readonly (string | undefined)[];
|
|
129
|
+
/** Per-step signed tree-size-2, for the steps that have one. */
|
|
130
|
+
signedSizes?: readonly (bigint | undefined)[];
|
|
107
131
|
} = {},
|
|
108
132
|
): Promise<CheckpointLink[]> {
|
|
109
133
|
let accumulator = opts.accumulatorFrom ?? [];
|
|
@@ -126,13 +150,13 @@ export async function foldProofChain(
|
|
|
126
150
|
`checkpoint chain is not contiguous at link ${i}: base ${p.treeSize1} != expected ${expectedBase}`,
|
|
127
151
|
);
|
|
128
152
|
}
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
|
|
153
|
+
// The trusted base size is a parameter, never read off the proof
|
|
154
|
+
// (ADR-0066 D5.4) — `expectedBase` is exactly that, already checked
|
|
155
|
+
// against `p.treeSize1` above. One link is one step, folded as a
|
|
156
|
+
// one-proof chain (see `singleProofChain`).
|
|
157
|
+
const signed = opts.signedSizes?.[i];
|
|
134
158
|
accumulator = await computeCheckpointAccumulator(
|
|
135
|
-
|
|
159
|
+
singleProofChain(p, signed),
|
|
136
160
|
accumulator,
|
|
137
161
|
expectedBase,
|
|
138
162
|
);
|
|
@@ -142,6 +166,7 @@ export async function foldProofChain(
|
|
|
142
166
|
accumulator,
|
|
143
167
|
proof: p,
|
|
144
168
|
};
|
|
169
|
+
if (signed !== undefined) link.signedTreeSize2 = signed;
|
|
145
170
|
const seal = opts.seals?.[i];
|
|
146
171
|
if (seal !== undefined) link.seal = seal;
|
|
147
172
|
const ref = opts.sourceRefs?.[i];
|
|
@@ -153,9 +178,17 @@ export async function foldProofChain(
|
|
|
153
178
|
}
|
|
154
179
|
|
|
155
180
|
/**
|
|
156
|
-
* Retained-`.sth` provider: decode each checkpoint's embedded consistency
|
|
157
|
-
* and fold. `checkpoints` are the raw `.sth` bytes in ascending massif
|
|
158
|
-
* `sourceRefs` optionally names them (e.g. filenames) for narration.
|
|
181
|
+
* Retained-`.sth` provider: decode each checkpoint's embedded consistency
|
|
182
|
+
* proofs and fold. `checkpoints` are the raw `.sth` bytes in ascending massif
|
|
183
|
+
* order; `sourceRefs` optionally names them (e.g. filenames) for narration.
|
|
184
|
+
* A checkpoint may relay several proofs under one signature (ADR-0066 D2);
|
|
185
|
+
* each becomes its own link, and only the LAST link of a checkpoint carries
|
|
186
|
+
* that checkpoint's seal, since the signature covers only the head size.
|
|
187
|
+
* A log's initialising proof `[0, n, [], peaks]` (empty consistency-paths:
|
|
188
|
+
* the empty tree has no peaks) is a valid first step only; `foldProofChain`
|
|
189
|
+
* binds each step's tree-size-1 to the size reached, so it verifies only
|
|
190
|
+
* against the empty tree. A checkpoint with no proof at all is malformed and
|
|
191
|
+
* the decode throws.
|
|
159
192
|
*/
|
|
160
193
|
export async function sthCheckpointChain(
|
|
161
194
|
checkpoints: readonly Uint8Array[],
|
|
@@ -165,20 +198,30 @@ export async function sthCheckpointChain(
|
|
|
165
198
|
sourceRefs?: readonly string[];
|
|
166
199
|
} = {},
|
|
167
200
|
): Promise<CheckpointLink[]> {
|
|
168
|
-
const proofs =
|
|
169
|
-
const seals: CheckpointSeal[] =
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
201
|
+
const proofs: FoldableConsistencyProof[] = [];
|
|
202
|
+
const seals: (CheckpointSeal | undefined)[] = [];
|
|
203
|
+
const sourceRefs: (string | undefined)[] = [];
|
|
204
|
+
const signedSizes: (bigint | undefined)[] = [];
|
|
205
|
+
checkpoints.forEach((bytes, i) => {
|
|
206
|
+
const chain = checkpointConsistencyProof(bytes);
|
|
207
|
+
chain.proofs.forEach((step, j) => {
|
|
208
|
+
const last = j === chain.proofs.length - 1;
|
|
209
|
+
proofs.push(step);
|
|
210
|
+
seals.push(last ? { kind: "sth", checkpointBytes: bytes } : undefined);
|
|
211
|
+
signedSizes.push(last ? chain.signedTreeSize2 : undefined);
|
|
212
|
+
sourceRefs.push(opts.sourceRefs?.[i]);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
173
215
|
return foldProofChain(proofs, {
|
|
174
216
|
seals,
|
|
217
|
+
signedSizes,
|
|
175
218
|
...(opts.accumulatorFrom !== undefined
|
|
176
219
|
? { accumulatorFrom: opts.accumulatorFrom }
|
|
177
220
|
: {}),
|
|
178
221
|
...(opts.accumulatorFromSize !== undefined
|
|
179
222
|
? { accumulatorFromSize: opts.accumulatorFromSize }
|
|
180
223
|
: {}),
|
|
181
|
-
...(opts.sourceRefs !== undefined ? { sourceRefs
|
|
224
|
+
...(opts.sourceRefs !== undefined ? { sourceRefs } : {}),
|
|
182
225
|
});
|
|
183
226
|
}
|
|
184
227
|
|
|
@@ -38,6 +38,7 @@ export type CompleteGrantReason =
|
|
|
38
38
|
| "checkpoint_parse_failed"
|
|
39
39
|
| "checkpoint_missing_peak_receipts"
|
|
40
40
|
| "checkpoint_missing_sealed_size"
|
|
41
|
+
| "checkpoint_consistency_invalid"
|
|
41
42
|
| "checkpoint_does_not_cover_leaf"
|
|
42
43
|
| "leaf_not_in_massif"
|
|
43
44
|
| "derive_failed";
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
* the log exists — no operator onboard token, authority flows down the log
|
|
10
10
|
* hierarchy (ADR-0053). Node/Bun HTTP only.
|
|
11
11
|
*/
|
|
12
|
+
import { responseProblemDetail } from "./http-problem.js";
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
/** Prepare (child public-root pre-registration) failure. */
|
|
14
16
|
export class PrepareLogError extends Error {
|
|
@@ -48,7 +50,7 @@ export async function prepareChildLog(
|
|
|
48
50
|
headers: { Authorization: `Forestrie-Grant ${params.grantBase64}` },
|
|
49
51
|
});
|
|
50
52
|
if (!res.ok) {
|
|
51
|
-
const preview =
|
|
53
|
+
const preview = await responseProblemDetail(res, 300);
|
|
52
54
|
throw new PrepareLogError(
|
|
53
55
|
`prepare failed: HTTP ${res.status} ${preview}`,
|
|
54
56
|
res.status,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildReceiptOffline,
|
|
3
|
+
checkpointConsistencyProof,
|
|
3
4
|
computeAccumulatorPeak,
|
|
4
5
|
openMassifNodeStore,
|
|
5
6
|
parseCheckpoint,
|
|
@@ -27,6 +28,7 @@ export type CreateReceiptReason =
|
|
|
27
28
|
| "checkpoint_parse_failed"
|
|
28
29
|
| "checkpoint_missing_peak_receipts"
|
|
29
30
|
| "checkpoint_missing_sealed_size"
|
|
31
|
+
| "checkpoint_consistency_invalid"
|
|
30
32
|
| "checkpoint_does_not_cover_leaf"
|
|
31
33
|
| "leaf_not_in_massif"
|
|
32
34
|
| "derive_failed";
|
|
@@ -123,6 +125,20 @@ export async function deriveCheckpointReceipt(input: {
|
|
|
123
125
|
"checkpoint carries no consistency proof (cannot determine sealed size)",
|
|
124
126
|
);
|
|
125
127
|
}
|
|
128
|
+
// The embedded consistency proofs must agree with the signed size: the
|
|
129
|
+
// last proof's tree-size-2 is the size the signature covers (ADR-0066 D1,
|
|
130
|
+
// D2). A checkpoint whose proofs contradict its signed size is malformed,
|
|
131
|
+
// and this builder refuses it rather than deriving from the signed half
|
|
132
|
+
// alone. (parseCheckpoint only requires that a proof be present.)
|
|
133
|
+
try {
|
|
134
|
+
checkpointConsistencyProof(checkpointBytes);
|
|
135
|
+
} catch (err) {
|
|
136
|
+
fail(
|
|
137
|
+
"parse",
|
|
138
|
+
"checkpoint_consistency_invalid",
|
|
139
|
+
`checkpoint consistency proofs do not agree with its signed size: ${errorMessage(err)}`,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
126
142
|
|
|
127
143
|
let store: MassifNodeStore;
|
|
128
144
|
try {
|
|
@@ -57,11 +57,11 @@ export const HEADER_LABELS: ReadonlyMap<number, LabelInfo> = new Map([
|
|
|
57
57
|
[CWT_CLAIMS_LABEL, { name: "CWT claims", note: "RFC 9597" }],
|
|
58
58
|
[
|
|
59
59
|
VDS_LABEL,
|
|
60
|
-
{ name: "verifiable data structure", note: "COSE receipts (
|
|
60
|
+
{ name: "verifiable data structure", note: "COSE receipts (RFC 9942)" },
|
|
61
61
|
],
|
|
62
62
|
[
|
|
63
63
|
VERIFIABLE_PROOFS_LABEL,
|
|
64
|
-
{ name: "verifiable proofs", note: "COSE receipts (
|
|
64
|
+
{ name: "verifiable proofs", note: "COSE receipts (RFC 9942)" },
|
|
65
65
|
],
|
|
66
66
|
[
|
|
67
67
|
DELEGATION_CERT_LABEL,
|
|
@@ -129,18 +129,19 @@ export const ALG_NAMES: ReadonlyMap<number, string> = new Map([
|
|
|
129
129
|
[-65799, "KS256 (secp256k1 + Keccak-256, forestrie private-use)"],
|
|
130
130
|
[
|
|
131
131
|
-65800,
|
|
132
|
-
"ES256-WebAuthn (forestrie private-use; delegation proofs
|
|
133
|
-
"
|
|
132
|
+
"ES256-WebAuthn (forestrie private-use; delegation proofs, certificates " +
|
|
133
|
+
"and session-key endorsements, never checkpoint-signing)",
|
|
134
134
|
],
|
|
135
135
|
]);
|
|
136
136
|
|
|
137
137
|
/**
|
|
138
|
-
* Verifiable data structure ids (
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* never as registry fact
|
|
138
|
+
* Verifiable data structure ids (the COSE receipts registry, RFC 9942).
|
|
139
|
+
* 3 is NOT a registered codepoint: draft-bryce-cose-receipts-mmr-profile
|
|
140
|
+
* requests TBD_1 (registration pending). The Go sealer emits `395: 3` on
|
|
141
|
+
* every checkpoint and peak receipt; the TypeScript exporters do not, so a
|
|
142
|
+
* verifier must not require it. Render it as the draft's unregistered
|
|
143
|
+
* codepoint, never as registry fact. The display strings are the protocol
|
|
144
|
+
* registry's (spec/label-registry.md).
|
|
144
145
|
*/
|
|
145
146
|
export const VDS_NAMES: ReadonlyMap<number, string> = new Map([
|
|
146
147
|
[1, "RFC9162_SHA256 (Certificate Transparency)"],
|
package/src/lib/delegate-flow.ts
CHANGED
|
@@ -33,6 +33,8 @@ import {
|
|
|
33
33
|
verifyDelegateKeyVoucher,
|
|
34
34
|
} from "./delegate-voucher.js";
|
|
35
35
|
|
|
36
|
+
import { responseProblemDetail } from "./http-problem.js";
|
|
37
|
+
|
|
36
38
|
/** Fixed inclusive MMR start for a horizon delegation lease. */
|
|
37
39
|
const MMR_START = 0;
|
|
38
40
|
|
|
@@ -170,7 +172,7 @@ export async function runDelegateFlow(
|
|
|
170
172
|
const pendingUrl = `${params.coordinatorUrl}/api/logs/${params.logId}/pending-delegation`;
|
|
171
173
|
const pendingRes = await fetchImpl(pendingUrl);
|
|
172
174
|
if (!pendingRes.ok) {
|
|
173
|
-
const preview =
|
|
175
|
+
const preview = await responseProblemDetail(pendingRes, 200);
|
|
174
176
|
throw new DelegateFlowError(
|
|
175
177
|
`pending-delegation fetch failed: HTTP ${pendingRes.status} ${preview}`,
|
|
176
178
|
pendingRes.status,
|
|
@@ -270,7 +272,7 @@ export async function runDelegateFlow(
|
|
|
270
272
|
}),
|
|
271
273
|
});
|
|
272
274
|
if (!submitRes.ok) {
|
|
273
|
-
const preview =
|
|
275
|
+
const preview = await responseProblemDetail(submitRes, 200);
|
|
274
276
|
throw new DelegateFlowError(
|
|
275
277
|
`certificate submit failed: HTTP ${submitRes.status} ${preview}`,
|
|
276
278
|
submitRes.status,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The human-readable reason a canopy / coordinator request was rejected
|
|
3
|
+
* (FOR-579).
|
|
4
|
+
*
|
|
5
|
+
* canopy-api answers with RFC 9457 problem details encoded as CBOR (served
|
|
6
|
+
* as `application/cbor` on most routes, `application/problem+cbor` on the
|
|
7
|
+
* SCRAPI ones); the delegation coordinator uses `application/problem+json`.
|
|
8
|
+
* Reading such a body with `response.text()` and printing it yields
|
|
9
|
+
* unreadable bytes, which is how a `400` from a strict CBOR decoder hid its
|
|
10
|
+
* cause (`Invalid CBOR body`) through four qualification runs on
|
|
11
|
+
* 2026-09-20. This decodes whichever form arrived and falls back to the
|
|
12
|
+
* text body. It never throws.
|
|
13
|
+
*/
|
|
14
|
+
import { decodeProblemDetailsBytes } from "@forestrie/scrapi-client";
|
|
15
|
+
|
|
16
|
+
/** Longest string returned; problem bodies are short, this is a guard. */
|
|
17
|
+
export const MAX_PROBLEM_DETAIL = 512;
|
|
18
|
+
|
|
19
|
+
function describe(problem: {
|
|
20
|
+
title?: string;
|
|
21
|
+
detail?: string;
|
|
22
|
+
}): string | undefined {
|
|
23
|
+
const { title, detail } = problem;
|
|
24
|
+
if (detail && title) return `${title}: ${detail}`;
|
|
25
|
+
return detail ?? title;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function responseProblemDetail(
|
|
29
|
+
response: Response,
|
|
30
|
+
max = MAX_PROBLEM_DETAIL,
|
|
31
|
+
): Promise<string> {
|
|
32
|
+
const contentType = (response.headers.get("content-type") ?? "").toLowerCase();
|
|
33
|
+
let text = "";
|
|
34
|
+
try {
|
|
35
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
36
|
+
if (bytes.length === 0) return "";
|
|
37
|
+
if (contentType.includes("cbor")) {
|
|
38
|
+
// No content type passed: canopy-api serves most problem bodies as
|
|
39
|
+
// plain application/cbor, so decode opportunistically.
|
|
40
|
+
const problem = decodeProblemDetailsBytes(bytes);
|
|
41
|
+
const described = problem && describe(problem);
|
|
42
|
+
if (described) return described.slice(0, max);
|
|
43
|
+
}
|
|
44
|
+
text = new TextDecoder().decode(bytes);
|
|
45
|
+
if (contentType.includes("json")) {
|
|
46
|
+
const described = describe(JSON.parse(text) as { title?: string; detail?: string });
|
|
47
|
+
if (described) return described.slice(0, max);
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
// fall through to whatever text we have
|
|
51
|
+
}
|
|
52
|
+
return text.slice(0, max);
|
|
53
|
+
}
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
calldataCheckpointChain,
|
|
40
40
|
sthCheckpointChain,
|
|
41
41
|
type CheckpointLink,
|
|
42
|
+
singleProofChain,
|
|
42
43
|
} from "./checkpoint-provider.js";
|
|
43
44
|
import { univocityLeafHash } from "./verify-anchored.js";
|
|
44
45
|
import { bytesEqual } from "./bytes.js";
|
|
@@ -102,16 +103,11 @@ async function emitFreshened(opts: {
|
|
|
102
103
|
const result = await freshenReceipt({
|
|
103
104
|
oldReceiptBytes: opts.oldReceiptBytes,
|
|
104
105
|
leafValue: opts.leafValue,
|
|
105
|
-
// `freshenReceipt`
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
// signed size.
|
|
111
|
-
consistencyProofs: opts.links.map((l) => ({
|
|
112
|
-
...l.proof,
|
|
113
|
-
signedTreeSize2: l.proof.treeSize2,
|
|
114
|
-
})),
|
|
106
|
+
// `freshenReceipt` takes chains; each link is one step, wrapped as a
|
|
107
|
+
// one-proof chain — see `singleProofChain` in checkpoint-provider.ts.
|
|
108
|
+
consistencyProofs: opts.links.map((l) =>
|
|
109
|
+
singleProofChain(l.proof, l.signedTreeSize2),
|
|
110
|
+
),
|
|
115
111
|
latestCheckpointBytes: opts.latestCheckpointBytes,
|
|
116
112
|
});
|
|
117
113
|
|
|
@@ -177,7 +173,9 @@ export async function freshenFromSthChain(opts: {
|
|
|
177
173
|
leafValue,
|
|
178
174
|
links,
|
|
179
175
|
latestCheckpointBytes: opts.checkpoints[opts.checkpoints.length - 1]!,
|
|
180
|
-
|
|
176
|
+
// One entry per LINK, as the calldata path reports: a checkpoint that
|
|
177
|
+
// relays several proofs yields several links that all name it.
|
|
178
|
+
sourceRefs: links.map((l) => l.sourceRef ?? ""),
|
|
181
179
|
knownAccumulator: opts.knownAccumulator,
|
|
182
180
|
});
|
|
183
181
|
}
|
|
@@ -59,7 +59,7 @@ export type AnchorCheck = OnChainLogState & {
|
|
|
59
59
|
* - the leaf is at/after the anchored size — the entry simply is not
|
|
60
60
|
* anchored yet (`receipt_newer_than_anchored_state`).
|
|
61
61
|
* Tampered receipts surface earlier (signature/inclusion) or as a
|
|
62
|
-
* recomputed peak that matches no anchored state ever; the chain
|
|
62
|
+
* recomputed peak that matches no anchored state ever; the chain roots
|
|
63
63
|
* that PROVE the buried case land in later plan phases.
|
|
64
64
|
*/
|
|
65
65
|
export function classifyUnanchoredPeak(
|
|
@@ -15,7 +15,7 @@ import type { AnchorCheck } from "./verify-anchored.js";
|
|
|
15
15
|
* (ADR-0056: every checkpoint's embedded consistency proof spans its
|
|
16
16
|
* massif's entry boundary to its seal). `@forestrie/receipt-verify`'s
|
|
17
17
|
* `verifyCheckpointChain` folds that chain from size 0, authenticating the
|
|
18
|
-
* accumulator at EVERY retained seal — this
|
|
18
|
+
* accumulator at EVERY retained seal — this root depends only on the
|
|
19
19
|
* public log store: no tiles, no RPC, no holder cache. It is the
|
|
20
20
|
* complement of the `CheckpointPublished` event scan (public chain data
|
|
21
21
|
* only); the two paths are independently sufficient and cross-checkable.
|
|
@@ -109,7 +109,7 @@ export type CheckpointChainAnchorCheck = AnchorCheck & {
|
|
|
109
109
|
|
|
110
110
|
/**
|
|
111
111
|
* Find the recomputed receipt peak in the authenticated chain. Newest-first
|
|
112
|
-
* (mirrors the event-scan
|
|
112
|
+
* (mirrors the event-scan route): the freshest cover gives the most useful
|
|
113
113
|
* report, and a match at ANY link is proof — later links' signed
|
|
114
114
|
* consistency proofs commit it forward. Newer-than-chain fails CLOSED with
|
|
115
115
|
* a refresh remedy: retention limits coverage, never validity.
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* record of those accumulators (`CheckpointPublished` carries `size` and
|
|
12
12
|
* `accumulator`; the contract stores nothing retrievable historically).
|
|
13
13
|
*
|
|
14
|
-
* This
|
|
15
|
-
* independent of the log store (the retained-checkpoint chain
|
|
14
|
+
* This route depends only on public blockchain data — deliberately
|
|
15
|
+
* independent of the log store (the retained-checkpoint chain root is its
|
|
16
16
|
* complement with the opposite dependency; see plan-2607-29).
|
|
17
17
|
*/
|
|
18
18
|
import { bytesEqual } from "./bytes.js";
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
} from "../lib/onboard-genesis-body.js";
|
|
11
11
|
import type { OnboardGenesisOptions } from "../options/onboard-genesis.js";
|
|
12
12
|
|
|
13
|
+
import { responseProblemDetail } from "../lib/http-problem.js";
|
|
14
|
+
|
|
13
15
|
/**
|
|
14
16
|
* FOR-406 (plan-2607-27 W2): operator genesis onboarding — turn a
|
|
15
17
|
* `forestrie deploy` into a usable forest. POSTs the five-label v2
|
|
@@ -138,7 +140,7 @@ export async function runOnboardGenesis(
|
|
|
138
140
|
command: "onboard-genesis",
|
|
139
141
|
message: `genesis POST rejected: HTTP ${res.status}`,
|
|
140
142
|
httpStatus: res.status,
|
|
141
|
-
detail:
|
|
143
|
+
detail: await responseProblemDetail(res),
|
|
142
144
|
});
|
|
143
145
|
return;
|
|
144
146
|
}
|
|
@@ -16,6 +16,8 @@ import { loadEs256SigningKey } from "../lib/sign-statement-key.js";
|
|
|
16
16
|
import { signX402Payment } from "../lib/x402-payment.js";
|
|
17
17
|
import type { OnboardRequestOptions } from "../options/onboard-request.js";
|
|
18
18
|
|
|
19
|
+
import { responseProblemDetail } from "../lib/http-problem.js";
|
|
20
|
+
|
|
19
21
|
/**
|
|
20
22
|
* Self-service onboarding (plan-2607-43 slice 06): create → (pay) → redeem.
|
|
21
23
|
*
|
|
@@ -183,7 +185,7 @@ export async function runOnboardRequest(
|
|
|
183
185
|
reportError(out, options, {
|
|
184
186
|
error: "create_failed",
|
|
185
187
|
command: "onboard-request",
|
|
186
|
-
message: `request rejected: HTTP ${res.status}: ${
|
|
188
|
+
message: `request rejected: HTTP ${res.status}: ${await responseProblemDetail(res, 300)}`,
|
|
187
189
|
httpStatus: res.status,
|
|
188
190
|
});
|
|
189
191
|
return;
|
|
@@ -271,7 +273,7 @@ export async function runOnboardRequest(
|
|
|
271
273
|
reportError(out, options, {
|
|
272
274
|
error: "redeem_failed",
|
|
273
275
|
command: "onboard-request",
|
|
274
|
-
message: `redeem rejected: HTTP ${res.status}: ${
|
|
276
|
+
message: `redeem rejected: HTTP ${res.status}: ${await responseProblemDetail(res, 300)}`,
|
|
275
277
|
httpStatus: res.status,
|
|
276
278
|
});
|
|
277
279
|
return;
|