@mikeargento/bitgraph 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/README.md +49 -40
  2. package/dist/__tests__/audit-anchors.test.d.ts +2 -0
  3. package/dist/__tests__/audit-anchors.test.d.ts.map +1 -0
  4. package/dist/__tests__/audit-anchors.test.js +152 -0
  5. package/dist/__tests__/audit-anchors.test.js.map +1 -0
  6. package/dist/__tests__/audit-anomalies.test.d.ts +2 -0
  7. package/dist/__tests__/audit-anomalies.test.d.ts.map +1 -0
  8. package/dist/__tests__/audit-anomalies.test.js +663 -0
  9. package/dist/__tests__/audit-anomalies.test.js.map +1 -0
  10. package/dist/__tests__/audit-attestation.test.d.ts +2 -0
  11. package/dist/__tests__/audit-attestation.test.d.ts.map +1 -0
  12. package/dist/__tests__/audit-attestation.test.js +403 -0
  13. package/dist/__tests__/audit-attestation.test.js.map +1 -0
  14. package/dist/__tests__/audit-bench-smoke.test.d.ts +2 -0
  15. package/dist/__tests__/audit-bench-smoke.test.d.ts.map +1 -0
  16. package/dist/__tests__/audit-bench-smoke.test.js +87 -0
  17. package/dist/__tests__/audit-bench-smoke.test.js.map +1 -0
  18. package/dist/__tests__/audit-boundary.test.d.ts +20 -0
  19. package/dist/__tests__/audit-boundary.test.d.ts.map +1 -0
  20. package/dist/__tests__/audit-boundary.test.js +84 -0
  21. package/dist/__tests__/audit-boundary.test.js.map +1 -0
  22. package/dist/__tests__/audit-cli.test.d.ts +2 -0
  23. package/dist/__tests__/audit-cli.test.d.ts.map +1 -0
  24. package/dist/__tests__/audit-cli.test.js +239 -0
  25. package/dist/__tests__/audit-cli.test.js.map +1 -0
  26. package/dist/__tests__/audit-dependencies.test.d.ts +2 -0
  27. package/dist/__tests__/audit-dependencies.test.d.ts.map +1 -0
  28. package/dist/__tests__/audit-dependencies.test.js +116 -0
  29. package/dist/__tests__/audit-dependencies.test.js.map +1 -0
  30. package/dist/__tests__/audit-fixtures.d.ts +265 -0
  31. package/dist/__tests__/audit-fixtures.d.ts.map +1 -0
  32. package/dist/__tests__/audit-fixtures.js +511 -0
  33. package/dist/__tests__/audit-fixtures.js.map +1 -0
  34. package/dist/__tests__/audit-ingest.test.d.ts +2 -0
  35. package/dist/__tests__/audit-ingest.test.d.ts.map +1 -0
  36. package/dist/__tests__/audit-ingest.test.js +473 -0
  37. package/dist/__tests__/audit-ingest.test.js.map +1 -0
  38. package/dist/__tests__/audit-memory-ingest.test.d.ts +12 -0
  39. package/dist/__tests__/audit-memory-ingest.test.d.ts.map +1 -0
  40. package/dist/__tests__/audit-memory-ingest.test.js +120 -0
  41. package/dist/__tests__/audit-memory-ingest.test.js.map +1 -0
  42. package/dist/__tests__/audit-property.test.d.ts +2 -0
  43. package/dist/__tests__/audit-property.test.d.ts.map +1 -0
  44. package/dist/__tests__/audit-property.test.js +771 -0
  45. package/dist/__tests__/audit-property.test.js.map +1 -0
  46. package/dist/__tests__/audit-real-fixtures.test.d.ts +2 -0
  47. package/dist/__tests__/audit-real-fixtures.test.d.ts.map +1 -0
  48. package/dist/__tests__/audit-real-fixtures.test.js +132 -0
  49. package/dist/__tests__/audit-real-fixtures.test.js.map +1 -0
  50. package/dist/__tests__/audit-real-live.test.d.ts +2 -0
  51. package/dist/__tests__/audit-real-live.test.d.ts.map +1 -0
  52. package/dist/__tests__/audit-real-live.test.js +107 -0
  53. package/dist/__tests__/audit-real-live.test.js.map +1 -0
  54. package/dist/__tests__/audit-reconstruct.test.d.ts +2 -0
  55. package/dist/__tests__/audit-reconstruct.test.d.ts.map +1 -0
  56. package/dist/__tests__/audit-reconstruct.test.js +287 -0
  57. package/dist/__tests__/audit-reconstruct.test.js.map +1 -0
  58. package/dist/__tests__/audit-report.test.d.ts +2 -0
  59. package/dist/__tests__/audit-report.test.d.ts.map +1 -0
  60. package/dist/__tests__/audit-report.test.js +186 -0
  61. package/dist/__tests__/audit-report.test.js.map +1 -0
  62. package/dist/__tests__/audit-roundtrip.test.d.ts +2 -0
  63. package/dist/__tests__/audit-roundtrip.test.d.ts.map +1 -0
  64. package/dist/__tests__/audit-roundtrip.test.js +368 -0
  65. package/dist/__tests__/audit-roundtrip.test.js.map +1 -0
  66. package/dist/__tests__/audit-temporal.test.d.ts +2 -0
  67. package/dist/__tests__/audit-temporal.test.d.ts.map +1 -0
  68. package/dist/__tests__/audit-temporal.test.js +364 -0
  69. package/dist/__tests__/audit-temporal.test.js.map +1 -0
  70. package/dist/__tests__/audit-tiers.test.d.ts +2 -0
  71. package/dist/__tests__/audit-tiers.test.d.ts.map +1 -0
  72. package/dist/__tests__/audit-tiers.test.js +282 -0
  73. package/dist/__tests__/audit-tiers.test.js.map +1 -0
  74. package/dist/__tests__/audit-witness-real.test.d.ts +19 -0
  75. package/dist/__tests__/audit-witness-real.test.d.ts.map +1 -0
  76. package/dist/__tests__/audit-witness-real.test.js +82 -0
  77. package/dist/__tests__/audit-witness-real.test.js.map +1 -0
  78. package/dist/__tests__/audit-witness.test.d.ts +2 -0
  79. package/dist/__tests__/audit-witness.test.d.ts.map +1 -0
  80. package/dist/__tests__/audit-witness.test.js +308 -0
  81. package/dist/__tests__/audit-witness.test.js.map +1 -0
  82. package/dist/__tests__/chain-hash-anchor.test.d.ts +22 -0
  83. package/dist/__tests__/chain-hash-anchor.test.d.ts.map +1 -0
  84. package/dist/__tests__/chain-hash-anchor.test.js +55 -0
  85. package/dist/__tests__/chain-hash-anchor.test.js.map +1 -0
  86. package/dist/__tests__/declared-proof-fixture.d.ts +66 -0
  87. package/dist/__tests__/declared-proof-fixture.d.ts.map +1 -0
  88. package/dist/__tests__/declared-proof-fixture.js +66 -0
  89. package/dist/__tests__/declared-proof-fixture.js.map +1 -0
  90. package/dist/__tests__/fuse-roundtrip.test.d.ts +2 -0
  91. package/dist/__tests__/fuse-roundtrip.test.d.ts.map +1 -0
  92. package/dist/__tests__/fuse-roundtrip.test.js +172 -0
  93. package/dist/__tests__/fuse-roundtrip.test.js.map +1 -0
  94. package/dist/__tests__/fuse-sdk.test.d.ts +2 -0
  95. package/dist/__tests__/fuse-sdk.test.d.ts.map +1 -0
  96. package/dist/__tests__/fuse-sdk.test.js +117 -0
  97. package/dist/__tests__/fuse-sdk.test.js.map +1 -0
  98. package/dist/__tests__/fuse-vectors.test.d.ts +2 -0
  99. package/dist/__tests__/fuse-vectors.test.d.ts.map +1 -0
  100. package/dist/__tests__/fuse-vectors.test.js +175 -0
  101. package/dist/__tests__/fuse-vectors.test.js.map +1 -0
  102. package/dist/__tests__/fuse-verify.test.d.ts +2 -0
  103. package/dist/__tests__/fuse-verify.test.d.ts.map +1 -0
  104. package/dist/__tests__/fuse-verify.test.js +250 -0
  105. package/dist/__tests__/fuse-verify.test.js.map +1 -0
  106. package/dist/__tests__/mock-proof-fixture.d.ts +15 -0
  107. package/dist/__tests__/mock-proof-fixture.d.ts.map +1 -0
  108. package/dist/__tests__/mock-proof-fixture.js +34 -0
  109. package/dist/__tests__/mock-proof-fixture.js.map +1 -0
  110. package/dist/__tests__/proof-hash-regression.test.js +4 -43
  111. package/dist/__tests__/proof-hash-regression.test.js.map +1 -1
  112. package/dist/__tests__/proof-hash.test.js +3 -21
  113. package/dist/__tests__/proof-hash.test.js.map +1 -1
  114. package/dist/__tests__/proof-integrity.test.d.ts +2 -0
  115. package/dist/__tests__/proof-integrity.test.d.ts.map +1 -0
  116. package/dist/__tests__/proof-integrity.test.js +426 -0
  117. package/dist/__tests__/proof-integrity.test.js.map +1 -0
  118. package/dist/__tests__/realistic-proof-fixture.d.ts +56 -0
  119. package/dist/__tests__/realistic-proof-fixture.d.ts.map +1 -0
  120. package/dist/__tests__/realistic-proof-fixture.js +57 -0
  121. package/dist/__tests__/realistic-proof-fixture.js.map +1 -0
  122. package/dist/__tests__/verifier.test.js +74 -0
  123. package/dist/__tests__/verifier.test.js.map +1 -1
  124. package/dist/fuse-cli.d.ts +3 -0
  125. package/dist/fuse-cli.d.ts.map +1 -0
  126. package/dist/fuse-cli.js +210 -0
  127. package/dist/fuse-cli.js.map +1 -0
  128. package/dist/fuse.d.ts +75 -0
  129. package/dist/fuse.d.ts.map +1 -0
  130. package/dist/fuse.js +273 -0
  131. package/dist/fuse.js.map +1 -0
  132. package/dist/index.d.ts +2 -0
  133. package/dist/index.d.ts.map +1 -1
  134. package/dist/index.js +4 -0
  135. package/dist/index.js.map +1 -1
  136. package/package.json +19 -9
  137. package/src/__tests__/audit-anchors.test.ts +188 -0
  138. package/src/__tests__/audit-anomalies.test.ts +815 -0
  139. package/src/__tests__/audit-attestation.test.ts +529 -0
  140. package/src/__tests__/audit-bench-smoke.test.ts +108 -0
  141. package/src/__tests__/audit-boundary.test.ts +96 -0
  142. package/src/__tests__/audit-cli.test.ts +288 -0
  143. package/src/__tests__/audit-dependencies.test.ts +141 -0
  144. package/src/__tests__/audit-fixtures.ts +736 -0
  145. package/src/__tests__/audit-ingest.test.ts +575 -0
  146. package/src/__tests__/audit-memory-ingest.test.ts +140 -0
  147. package/src/__tests__/audit-property.test.ts +892 -0
  148. package/src/__tests__/audit-real-fixtures.test.ts +153 -0
  149. package/src/__tests__/audit-real-live.test.ts +128 -0
  150. package/src/__tests__/audit-reconstruct.test.ts +353 -0
  151. package/src/__tests__/audit-report.test.ts +222 -0
  152. package/src/__tests__/audit-roundtrip.test.ts +464 -0
  153. package/src/__tests__/audit-temporal.test.ts +446 -0
  154. package/src/__tests__/audit-tiers.test.ts +343 -0
  155. package/src/__tests__/audit-witness-real.test.ts +92 -0
  156. package/src/__tests__/audit-witness.test.ts +345 -0
  157. package/src/__tests__/chain-hash-anchor.test.ts +67 -0
  158. package/src/__tests__/declared-proof-fixture.ts +65 -0
  159. package/src/__tests__/fuse-roundtrip.test.ts +174 -0
  160. package/src/__tests__/fuse-sdk.test.ts +142 -0
  161. package/src/__tests__/fuse-vectors.test.ts +218 -0
  162. package/src/__tests__/fuse-verify.test.ts +278 -0
  163. package/src/__tests__/mock-proof-fixture.ts +36 -0
  164. package/src/__tests__/proof-hash-regression.test.ts +4 -44
  165. package/src/__tests__/proof-hash.test.ts +3 -22
  166. package/src/__tests__/proof-integrity.test.ts +541 -0
  167. package/src/__tests__/real-fixtures/anchor-after-112762.json +47 -0
  168. package/src/__tests__/real-fixtures/anchor-with-ethereum-112758.json +47 -0
  169. package/src/__tests__/real-fixtures/live-proof-0.json +42 -0
  170. package/src/__tests__/real-fixtures/live-proof-1.json +43 -0
  171. package/src/__tests__/real-fixtures/live-proof-2.json +43 -0
  172. package/src/__tests__/real-fixtures/successor-112760.json +38 -0
  173. package/src/__tests__/real-fixtures/witness-25525146.json +6 -0
  174. package/src/__tests__/real-fixtures/witness-25525147.json +6 -0
  175. package/src/__tests__/realistic-proof-fixture.ts +57 -0
  176. package/src/__tests__/verifier.test.ts +83 -0
  177. package/src/fuse-cli.ts +187 -0
  178. package/src/fuse.ts +356 -0
  179. package/src/index.ts +6 -0
  180. package/dist/canonical.d.ts +0 -54
  181. package/dist/canonical.d.ts.map +0 -1
  182. package/dist/canonical.js +0 -119
  183. package/dist/canonical.js.map +0 -1
  184. package/dist/proof-hash.d.ts +0 -9
  185. package/dist/proof-hash.d.ts.map +0 -1
  186. package/dist/proof-hash.js +0 -75
  187. package/dist/proof-hash.js.map +0 -1
  188. package/dist/types.d.ts +0 -705
  189. package/dist/types.d.ts.map +0 -1
  190. package/dist/types.js +0 -3
  191. package/dist/types.js.map +0 -1
  192. package/dist/verifier.d.ts +0 -27
  193. package/dist/verifier.d.ts.map +0 -1
  194. package/dist/verifier.js +0 -844
  195. package/dist/verifier.js.map +0 -1
package/dist/fuse.js ADDED
@@ -0,0 +1,273 @@
1
+ // Copyright (c) Mike Argento. All rights reserved. See LICENSE.
2
+ /**
3
+ * fuse(builder, options): the producer interface of the bitgraph-fuse/1
4
+ * profile (working name; outwardly this is simply BitGraph).
5
+ *
6
+ * The four beats, in order, with nothing else in between:
7
+ * 1. nonce: allocate a slot; the enclave signs a record that contains no
8
+ * artifact data and hands back a nonce that is a bearer ticket
9
+ * until it is consumed.
10
+ * 2. fuse: hand the COMMITMENT to that record (never the raw nonce) to the
11
+ * builder, which writes it into the artifact it is producing and
12
+ * returns the finished bytes.
13
+ * 3. hash: SHA-256 of the fused bytes.
14
+ * 4. fill: commit that digest under the same slot, with the placement id
15
+ * and the origin digest in the signed attribution.
16
+ *
17
+ * What this module never does: write the nonce anywhere but process memory,
18
+ * put it in a message, or fall back to an ordinary recording when the fused
19
+ * commit fails. A failure is reported as a failure and the slot expires on
20
+ * its own.
21
+ *
22
+ * Transport: the site's proxy routes by default (POST /api/fuse/allocate and
23
+ * /api/fuse/commit, behind the anchor-first gate), configurable so a licensee
24
+ * can point it at a parent directly.
25
+ */
26
+ import { sha256 } from "@noble/hashes/sha256";
27
+ import { buildFrame, bytesToBase64, bytesToHex, computeSlotCommitment, computeSlotRecordHash, fuseAttribution, getPlacement, verifyFuse, base64ToBytes, } from "@mikeargento/bitgraph-verify";
28
+ export class FuseError extends Error {
29
+ code;
30
+ status;
31
+ constructor(code, message, status = null) {
32
+ super(message);
33
+ this.name = "FuseError";
34
+ this.code = code;
35
+ this.status = status;
36
+ }
37
+ }
38
+ /** A builder for a registered placement over an existing original (Forms A and B), or for a bare Form C payload. */
39
+ export function builderFor(placement, original) {
40
+ const p = getPlacement(placement);
41
+ if (p === undefined)
42
+ throw new FuseError("bad-placement", `placement "${placement}" is not registered`);
43
+ return ({ commitment, originDigest }) => {
44
+ if (original !== undefined)
45
+ return p.build({ original, originDigest: originDigest ?? sha256(original), commitment });
46
+ return p.build(originDigest !== undefined ? { originDigest, commitment } : { commitment });
47
+ };
48
+ }
49
+ const DEFAULTS = {
50
+ baseUrl: "https://bitgraph.ing",
51
+ allocatePath: "/api/fuse/allocate",
52
+ commitPath: "/api/fuse/commit",
53
+ lookupPath: "/api/proofs/",
54
+ timeoutMs: 30_000,
55
+ recoveryAttempts: 5,
56
+ recoveryDelayMs: 1_500,
57
+ };
58
+ const B64_32 = /^[A-Za-z0-9+/]{43}=$/;
59
+ const B64_64 = /^[A-Za-z0-9+/]{86}==$/;
60
+ function isSlotRecord(x) {
61
+ if (x === null || typeof x !== "object" || Array.isArray(x))
62
+ return false;
63
+ const s = x;
64
+ return (s.version === "bitgraph/slot/1" &&
65
+ typeof s.nonceB64 === "string" && B64_32.test(s.nonceB64) &&
66
+ typeof s.counter === "string" && /^(0|[1-9][0-9]*)$/.test(s.counter) &&
67
+ typeof s.epochId === "string" && typeof s.publicKeyB64 === "string" &&
68
+ typeof s.signatureB64 === "string" && B64_64.test(s.signatureB64));
69
+ }
70
+ function toUrlSafe(b64) {
71
+ return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
72
+ }
73
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
74
+ async function request(t, path, init) {
75
+ const f = t.fetch ?? fetch;
76
+ const headers = {};
77
+ if (init.body !== undefined)
78
+ headers["Content-Type"] = "application/json";
79
+ if (t.apiKey)
80
+ headers["Authorization"] = `Bearer ${t.apiKey}`;
81
+ let res;
82
+ try {
83
+ res = await f(`${t.baseUrl}${path}`, {
84
+ method: init.method,
85
+ headers,
86
+ ...(init.body !== undefined ? { body: JSON.stringify(init.body) } : {}),
87
+ signal: AbortSignal.timeout(t.timeoutMs),
88
+ });
89
+ }
90
+ catch (err) {
91
+ throw new FuseError("network", `request failed: ${err instanceof Error ? err.message : String(err)}`);
92
+ }
93
+ const text = await res.text();
94
+ let json = null;
95
+ try {
96
+ json = text.length > 0 ? JSON.parse(text) : null;
97
+ }
98
+ catch {
99
+ json = text;
100
+ }
101
+ return { status: res.status, json, headers: res.headers };
102
+ }
103
+ function messageOf(json, fallback) {
104
+ return json !== null && typeof json === "object" && typeof json.error === "string" ? json.error : fallback;
105
+ }
106
+ function codeOf(json) {
107
+ return json !== null && typeof json === "object" && typeof json.code === "string" ? json.code : null;
108
+ }
109
+ /**
110
+ * Read the proof back by digest after a lost or refused commit: the ONE proof
111
+ * whose commit.slotHashB64 is the hash of the slot record we hold. Any other
112
+ * proof of the same digest is a different recording under a different slot.
113
+ * Never allocates.
114
+ */
115
+ async function recover(t, artifactDigestB64, slot) {
116
+ const expectedSlotHash = bytesToBase64(computeSlotRecordHash(slot));
117
+ for (let attempt = 0; attempt < t.recoveryAttempts; attempt++) {
118
+ if (attempt > 0)
119
+ await sleep(t.recoveryDelayMs);
120
+ let r;
121
+ try {
122
+ r = await request(t, `${t.lookupPath}${encodeURIComponent(toUrlSafe(artifactDigestB64))}`, { method: "GET" });
123
+ }
124
+ catch {
125
+ continue;
126
+ }
127
+ if (r.status !== 200)
128
+ continue;
129
+ const proofs = r.json?.proofs;
130
+ if (!Array.isArray(proofs))
131
+ continue;
132
+ for (const entry of proofs) {
133
+ const p = entry?.proof;
134
+ if (p && p.commit?.slotHashB64 === expectedSlotHash && p.commit?.nonceB64 === slot.nonceB64)
135
+ return p;
136
+ }
137
+ }
138
+ return null;
139
+ }
140
+ /**
141
+ * Allocate, fuse, hash, fill. Returns the Frame with the unchanged proof, or
142
+ * throws a FuseError; it never returns an ordinary recording in place of a
143
+ * fused one.
144
+ */
145
+ export async function fuse(builder, options) {
146
+ const placement = getPlacement(options.placement);
147
+ if (placement === undefined)
148
+ throw new FuseError("bad-placement", `placement "${options.placement}" is not registered`);
149
+ if (placement.form !== "C" && options.original === undefined)
150
+ throw new FuseError("bad-input", `${placement.id} needs the original bytes`);
151
+ if (placement.form === "C" && options.original !== undefined)
152
+ throw new FuseError("bad-input", "produced/1 takes no original; pass originDigest to name a source");
153
+ if (options.originDigest !== undefined && options.originDigest.length !== 32)
154
+ throw new FuseError("bad-input", "originDigest must be 32 bytes");
155
+ const t = { ...DEFAULTS, ...(options.transport ?? {}) };
156
+ const originDigest = options.original !== undefined ? sha256(options.original) : options.originDigest;
157
+ const originDigestB64 = originDigest !== undefined ? bytesToBase64(originDigest) : null;
158
+ // 1. nonce
159
+ const alloc = await request(t, t.allocatePath, { method: "POST", body: {} });
160
+ if (alloc.status === 503 && codeOf(alloc.json) === "tee-restarting")
161
+ throw new FuseError("tee-restarting", messageOf(alloc.json, "the boundary is restarting"), 503);
162
+ if (alloc.status !== 200)
163
+ throw new FuseError("allocate-failed", messageOf(alloc.json, `allocation failed (${alloc.status})`), alloc.status);
164
+ const slotId = alloc.json?.slotId;
165
+ const slot = alloc.json?.slot;
166
+ if (!isSlotRecord(slot) || slotId !== slot.nonceB64)
167
+ throw new FuseError("allocate-failed", "the allocation response is not a slot record", alloc.status);
168
+ if (slot.chainId !== "bitgraph:main")
169
+ throw new FuseError("allocate-failed", "the slot is not on the anchored chain; a fused floor needs bitgraph:main");
170
+ // 2. fuse
171
+ const commitment = computeSlotCommitment(slot);
172
+ let fused;
173
+ try {
174
+ fused = await builder({ commitment, commitmentHex: bytesToHex(commitment), ...(originDigest !== undefined ? { originDigest } : {}), slot });
175
+ }
176
+ catch (err) {
177
+ throw new FuseError("builder-failed", `the builder threw: ${err instanceof Error ? err.message : String(err)}`);
178
+ }
179
+ if (!(fused instanceof Uint8Array))
180
+ throw new FuseError("builder-failed", "the builder must return a Uint8Array");
181
+ // Fail closed: never commit bytes that do not carry the commitment.
182
+ const located = placement.locate(fused);
183
+ if (located === null || bytesToHex(located.commitment) !== bytesToHex(commitment)) {
184
+ throw new FuseError("commitment-missing", `the fused bytes do not carry the ${placement.id} commitment; nothing was committed and the slot will expire`);
185
+ }
186
+ // 3. hash
187
+ const artifactDigest = sha256(fused);
188
+ const artifactDigestB64 = bytesToBase64(artifactDigest);
189
+ // 4. fill
190
+ const attribution = fuseAttribution(placement.id, originDigest);
191
+ const body = {
192
+ digests: [{ digestB64: artifactDigestB64, hashAlg: "sha256" }],
193
+ slotId: slot.nonceB64,
194
+ slot,
195
+ chainId: "bitgraph:main",
196
+ attribution,
197
+ };
198
+ if (options.agency !== undefined)
199
+ body.agency = options.agency;
200
+ let proof = null;
201
+ let recovered = false;
202
+ let commit = null;
203
+ try {
204
+ commit = await request(t, t.commitPath, { method: "POST", body });
205
+ }
206
+ catch (err) {
207
+ // The request may have reached the boundary. Read back before giving up; never allocate again.
208
+ proof = await recover(t, artifactDigestB64, slot);
209
+ if (proof === null)
210
+ throw err;
211
+ recovered = true;
212
+ }
213
+ if (proof === null && commit !== null) {
214
+ if (commit.status === 200) {
215
+ const j = commit.json;
216
+ proof = Array.isArray(j) ? (j[0] ?? null) : (j?.proof ?? null);
217
+ if (proof === null)
218
+ throw new FuseError("commit-refused", "the commit response carried no proof", 200);
219
+ }
220
+ else if (commit.status === 409 && codeOf(commit.json) === "slot-unavailable") {
221
+ proof = await recover(t, artifactDigestB64, slot);
222
+ if (proof === null)
223
+ throw new FuseError("slot-unavailable", messageOf(commit.json, "the slot is no longer available"), 409);
224
+ recovered = true;
225
+ }
226
+ else if (commit.status === 503 && codeOf(commit.json) === "tee-restarting") {
227
+ proof = await recover(t, artifactDigestB64, slot);
228
+ if (proof === null)
229
+ throw new FuseError("tee-restarting", messageOf(commit.json, "the boundary is restarting"), 503);
230
+ recovered = true;
231
+ }
232
+ else {
233
+ throw new FuseError("commit-refused", messageOf(commit.json, `commit refused (${commit.status})`), commit.status);
234
+ }
235
+ }
236
+ if (proof === null)
237
+ throw new FuseError("transport", "no proof");
238
+ // Never label as fused a proof under any other slot.
239
+ if (proof.slotAllocation?.nonceB64 !== slot.nonceB64 || proof.commit?.nonceB64 !== slot.nonceB64) {
240
+ throw new FuseError("slot-mismatch", "the boundary returned a proof under a different slot; nothing is labelled fused");
241
+ }
242
+ // A minted proof is verified by a reader before it is called a proof.
243
+ const verification = await verifyFuse({ proof, bytes: fused });
244
+ if (verification.category !== "FUSED_DIRECT") {
245
+ throw new FuseError("verification-failed", `the returned proof does not verify as fused: ${verification.category}${verification.reason ? ` (${verification.reason})` : ""}`);
246
+ }
247
+ const frame = buildFrame({
248
+ proof,
249
+ placement: placement.id,
250
+ artifactDigest,
251
+ ...(originDigest !== undefined ? { originDigest } : {}),
252
+ fusedFile: options.fusedFile ?? null,
253
+ ...(placement.form === "C" ? { fusePayload: fused } : {}),
254
+ });
255
+ const keep = options.keepFused ?? !placement.byteExact;
256
+ return {
257
+ frame,
258
+ proof,
259
+ artifactDigestB64,
260
+ originDigestB64,
261
+ ...(keep ? { fusedBytes: fused } : {}),
262
+ recovered,
263
+ verification,
264
+ };
265
+ }
266
+ /** Decode a standard-base64 digest, for callers holding one as text. */
267
+ export function digestFromBase64(b64) {
268
+ const d = base64ToBytes(b64);
269
+ if (d === null || d.length !== 32)
270
+ throw new FuseError("bad-input", "not a 32-byte base64 digest");
271
+ return d;
272
+ }
273
+ //# sourceMappingURL=fuse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fuse.js","sourceRoot":"","sources":["../src/fuse.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -15,6 +15,8 @@ export { computeProofHash } from "@mikeargento/bitgraph-verify";
15
15
  export { canonicalize, canonicalizeToString, constantTimeEqual } from "@mikeargento/bitgraph-verify";
16
16
  export type { HostCapabilities } from "./host.js";
17
17
  export { Constructor } from "./constructor.js";
18
+ export { fuse, builderFor, FuseError, digestFromBase64 } from "./fuse.js";
19
+ export type { FuseBuilder, BuilderInput, FuseOptions, FuseResult, FuseTransport, FuseErrorCode } from "./fuse.js";
18
20
  export { parsePolicy, hashPolicy, createPolicyBinding, validateAction, } from "./policy.js";
19
21
  export type { PolicyDocument, PolicyRules, ActionValidationResult, } from "./policy.js";
20
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAGH,YAAY,EACV,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,eAAe,EACf,WAAW,EACX,aAAa,EACb,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,cAAc,GACf,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAC3E,YAAY,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGrG,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAGlD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,OAAO,EACL,WAAW,EACX,UAAU,EACV,mBAAmB,EACnB,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,cAAc,EACd,WAAW,EACX,sBAAsB,GACvB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAGH,YAAY,EACV,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,eAAe,EACf,WAAW,EACX,aAAa,EACb,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,cAAc,GACf,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAC3E,YAAY,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGrG,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAGlD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAK/C,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC1E,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAGlH,OAAO,EACL,WAAW,EACX,UAAU,EACV,mBAAmB,EACnB,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,cAAc,EACd,WAAW,EACX,sBAAsB,GACvB,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -4,6 +4,10 @@ export { computeProofHash } from "@mikeargento/bitgraph-verify";
4
4
  export { canonicalize, canonicalizeToString, constantTimeEqual } from "@mikeargento/bitgraph-verify";
5
5
  // Constructor (write path)
6
6
  export { Constructor } from "./constructor.js";
7
+ // The producer profile over the primitive (working name Fuse): allocate a
8
+ // slot, write a commitment to it into the artifact, hash, commit under the
9
+ // same slot. The resulting proof is ordinary bitgraph/1.
10
+ export { fuse, builderFor, FuseError, digestFromBase64 } from "./fuse.js";
7
11
  // Policy parsing, hashing, and validation
8
12
  export { parsePolicy, hashPolicy, createPolicyBinding, validateAction, } from "./policy.js";
9
13
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gEAAgE;AA4BhE,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAE3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAKrG,2BAA2B;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,0CAA0C;AAC1C,OAAO,EACL,WAAW,EACX,UAAU,EACV,mBAAmB,EACnB,cAAc,GACf,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gEAAgE;AA4BhE,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAE3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAKrG,2BAA2B;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,0EAA0E;AAC1E,2EAA2E;AAC3E,yDAAyD;AACzD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAG1E,0CAA0C;AAC1C,OAAO,EACL,WAAW,EACX,UAAU,EACV,mBAAmB,EACnB,cAAc,GACf,MAAM,aAAa,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikeargento/bitgraph",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "description": "Portable cryptographic proof of construction for digital artifacts.",
6
6
  "type": "module",
@@ -17,24 +17,34 @@
17
17
  "src",
18
18
  "README.md",
19
19
  "LICENSE",
20
- "NOTICE"
20
+ "NOTICE",
21
+ "!src/__tests__/fuse-fixtures"
21
22
  ],
22
23
  "scripts": {
23
- "build": "tsc",
24
- "typecheck": "tsc --noEmit",
25
- "test:core": "tsc && node --test dist/__tests__/canonical.test.js dist/__tests__/constructor.test.js dist/__tests__/verifier.test.js",
26
- "test": "npm run test:core"
24
+ "build": "npm run build --workspace=packages/verify && npm run build --workspace=packages/audit && npm run build --workspace=packages/player && npm run build --workspace=packages/mcp && tsc",
25
+ "typecheck": "npm run build --workspace=packages/verify && npm run build --workspace=packages/audit && npm run typecheck --workspace=packages/mcp && tsc --noEmit",
26
+ "test:core": "npm run build && node --test dist/__tests__/canonical.test.js dist/__tests__/constructor.test.js dist/__tests__/verifier.test.js dist/__tests__/proof-integrity.test.js dist/__tests__/audit-ingest.test.js dist/__tests__/audit-tiers.test.js dist/__tests__/audit-reconstruct.test.js dist/__tests__/audit-anomalies.test.js dist/__tests__/audit-anchors.test.js dist/__tests__/audit-witness.test.js dist/__tests__/audit-temporal.test.js dist/__tests__/audit-attestation.test.js dist/__tests__/audit-report.test.js dist/__tests__/audit-cli.test.js dist/__tests__/audit-property.test.js dist/__tests__/audit-real-fixtures.test.js dist/__tests__/audit-dependencies.test.js dist/__tests__/audit-bench-smoke.test.js dist/__tests__/audit-roundtrip.test.js dist/__tests__/audit-real-live.test.js dist/__tests__/chain-hash-anchor.test.js dist/__tests__/audit-boundary.test.js dist/__tests__/audit-witness-real.test.js dist/__tests__/audit-memory-ingest.test.js dist/__tests__/fuse-vectors.test.js dist/__tests__/fuse-roundtrip.test.js dist/__tests__/fuse-verify.test.js dist/__tests__/fuse-sdk.test.js",
27
+ "test": "npm run test:core",
28
+ "prepack": "npm run build"
27
29
  },
28
30
  "dependencies": {
29
- "@mikeargento/bitgraph-verify": "^1.0.0",
31
+ "@mikeargento/bitgraph-verify": "^1.4.0",
30
32
  "@noble/ed25519": "^2.1.0",
31
33
  "@noble/hashes": "^1.4.0"
32
34
  },
33
35
  "devDependencies": {
36
+ "@mikeargento/bitgraph-audit": "^0.4.0",
34
37
  "@types/node": "^20.0.0",
35
38
  "typescript": "^5.4.0"
36
39
  },
37
40
  "workspaces": [
38
- "packages/verify"
39
- ]
41
+ "packages/verify",
42
+ "packages/audit",
43
+ "packages/mcp",
44
+ "packages/player",
45
+ "packages/titles"
46
+ ],
47
+ "bin": {
48
+ "bitgraph-fuse": "./dist/fuse-cli.js"
49
+ }
40
50
  }
@@ -0,0 +1,188 @@
1
+ // Copyright (c) Mike Argento. All rights reserved. See LICENSE.
2
+
3
+ /**
4
+ * Anchor identification tests (G5): the SIGNED attribution.name is the
5
+ * discriminator; unsigned metadata.type is corroboration only, recorded
6
+ * but never trusted; the block number comes only from the signed
7
+ * Etherscan URL; no wall-clock time is ever derived from anything here.
8
+ */
9
+
10
+ import { describe, it } from "node:test";
11
+ import * as assert from "node:assert/strict";
12
+ import {
13
+ identifyAnchors,
14
+ ingestBundle,
15
+ verifyObservedProofs,
16
+ } from "@mikeargento/bitgraph-audit";
17
+ import type { IngestResult } from "@mikeargento/bitgraph-audit";
18
+ import {
19
+ makeAnchorProof,
20
+ makeChainIdProof,
21
+ makeTempDir,
22
+ proofJson,
23
+ writeBundleDir,
24
+ } from "./audit-fixtures.js";
25
+
26
+ const BLOCK_HASH = "0x28ed3639cd705fb8cb2b915c1991e9f808b40e775bc8eb540702942729fec2c0";
27
+
28
+ async function ingestAndVerify(files: Record<string, string | Uint8Array>): Promise<IngestResult> {
29
+ const dir = await makeTempDir("bg-audit-anchors-");
30
+ await writeBundleDir(dir, files);
31
+ const ingest = await ingestBundle(dir);
32
+ await verifyObservedProofs(ingest);
33
+ return ingest;
34
+ }
35
+
36
+ describe("audit anchors: identification by signed attribution", () => {
37
+ it("identifies an anchor by attribution.name and extracts the signed facts", async () => {
38
+ const anchor = await makeAnchorProof({
39
+ blockHash: BLOCK_HASH,
40
+ blockNumber: "24800448",
41
+ epochId: "EPOCH-A",
42
+ counter: "4",
43
+ slotCounter: "3",
44
+ chainId: "bitgraph:main",
45
+ metadata: {
46
+ type: "ethereum-anchor",
47
+ anchor: { blockNumber: 24800448, blockHash: BLOCK_HASH, blockTime: 1750000000 },
48
+ },
49
+ });
50
+ const user = await makeChainIdProof({ chainId: "bitgraph:main", counter: "6", epochId: "EPOCH-A" });
51
+
52
+ const ingest = await ingestAndVerify({
53
+ "proofs/anchor.json": proofJson(anchor.proof),
54
+ "proofs/user.json": proofJson(user.proof),
55
+ });
56
+ const result = identifyAnchors(ingest);
57
+
58
+ assert.equal(result.anchors.length, 1);
59
+ const record = result.anchors[0]!;
60
+ assert.equal(record.proofHash, anchor.proofHash);
61
+ assert.equal(record.epochId, "EPOCH-A");
62
+ assert.equal(record.chainId, "bitgraph:main");
63
+ assert.equal(record.counter, "4");
64
+ assert.equal(record.slotCounter, "3");
65
+ assert.equal(record.blockHash, BLOCK_HASH);
66
+ assert.equal(record.blockNumber, "24800448");
67
+ assert.equal(record.metadataCorroboration, "agrees");
68
+ // The unsigned metadata carries a blockTime; no time field of any kind
69
+ // may appear on the record.
70
+ assert.ok(!("timestamp" in record));
71
+ assert.ok(!("blockTime" in record));
72
+ assert.equal(result.findings.length, 0);
73
+ });
74
+
75
+ it("copies the run verification status onto the record", async () => {
76
+ const good = await makeAnchorProof({ blockHash: BLOCK_HASH, blockNumber: "1", epochId: "E", counter: "2" });
77
+ const bad = await makeAnchorProof({ blockHash: BLOCK_HASH.replace("28", "29"), blockNumber: "2", epochId: "E", counter: "4" });
78
+ // Tamper the signature after signing: identification still works (it
79
+ // reads the signed body fields), and the failed status is copied.
80
+ bad.proof.signer.signatureB64 = `AA${bad.proof.signer.signatureB64.slice(2)}`;
81
+
82
+ const ingest = await ingestAndVerify({
83
+ "proofs/good.json": proofJson(good.proof),
84
+ "proofs/bad.json": proofJson(bad.proof),
85
+ });
86
+ const result = identifyAnchors(ingest);
87
+
88
+ assert.equal(result.anchors.length, 2);
89
+ const goodRecord = result.anchors.find((a) => a.proofHash === good.proofHash)!;
90
+ assert.equal(goodRecord.verificationTier, "integrity");
91
+ assert.equal(goodRecord.verificationStatus, "artifact-unavailable");
92
+ const badRecord = result.anchors.find((a) => a.proofHash !== good.proofHash)!;
93
+ assert.equal(badRecord.verificationStatus, "failed");
94
+ assert.ok(badRecord.verificationReason !== undefined);
95
+ });
96
+
97
+ it("never identifies an anchor from unsigned metadata alone", async () => {
98
+ const user = await makeChainIdProof({ counter: "2", epochId: "E-META" });
99
+ (user.proof as unknown as Record<string, unknown>)["metadata"] = { type: "ethereum-anchor" };
100
+
101
+ const ingest = await ingestAndVerify({ "proofs/claimant.json": proofJson(user.proof) });
102
+ const result = identifyAnchors(ingest);
103
+
104
+ assert.equal(result.anchors.length, 0);
105
+ assert.equal(result.metadataOnlyProofHashes.length, 1);
106
+ const finding = result.findings.find((f) => f.code === "anchor-metadata-only-claim");
107
+ assert.ok(finding !== undefined);
108
+ assert.match(finding.message, /not treated as an anchor/);
109
+ });
110
+
111
+ it("records disagreement between the signed attribution and unsigned metadata", async () => {
112
+ const anchor = await makeAnchorProof({
113
+ blockHash: BLOCK_HASH,
114
+ blockNumber: "7",
115
+ epochId: "E-DIS",
116
+ counter: "2",
117
+ metadata: { type: "something-else" },
118
+ });
119
+ const ingest = await ingestAndVerify({ "proofs/anchor.json": proofJson(anchor.proof) });
120
+ const result = identifyAnchors(ingest);
121
+
122
+ assert.equal(result.anchors.length, 1, "the signed field governs: still an anchor");
123
+ assert.equal(result.anchors[0]!.metadataCorroboration, "disagrees");
124
+ assert.ok(result.findings.some((f) => f.code === "anchor-metadata-disagreement"));
125
+ });
126
+
127
+ it("treats absent metadata as absent corroboration, not disagreement", async () => {
128
+ const anchor = await makeAnchorProof({ blockHash: BLOCK_HASH, blockNumber: "9", epochId: "E-ABS", counter: "2" });
129
+ const ingest = await ingestAndVerify({ "proofs/anchor.json": proofJson(anchor.proof) });
130
+ const result = identifyAnchors(ingest);
131
+ assert.equal(result.anchors[0]!.metadataCorroboration, "absent");
132
+ assert.equal(result.findings.filter((f) => f.code === "anchor-metadata-disagreement").length, 0);
133
+ });
134
+ });
135
+
136
+ describe("audit anchors: block number from the signed title URL", () => {
137
+ it("parses the number only from a strict Etherscan block URL", async () => {
138
+ const good = await makeAnchorProof({ blockHash: BLOCK_HASH, blockNumber: "24800448", epochId: "E-URL", counter: "2" });
139
+ const foreign = await makeAnchorProof({
140
+ blockHash: BLOCK_HASH.replace("28", "30"),
141
+ title: "https://example.com/block/99",
142
+ epochId: "E-URL",
143
+ counter: "4",
144
+ });
145
+ const missing = await makeAnchorProof({
146
+ blockHash: BLOCK_HASH.replace("28", "31"),
147
+ noTitle: true,
148
+ epochId: "E-URL",
149
+ counter: "6",
150
+ });
151
+
152
+ const ingest = await ingestAndVerify({
153
+ "proofs/good.json": proofJson(good.proof),
154
+ "proofs/foreign.json": proofJson(foreign.proof),
155
+ "proofs/missing.json": proofJson(missing.proof),
156
+ });
157
+ const result = identifyAnchors(ingest);
158
+
159
+ const byHash = new Map(result.anchors.map((a) => [a.proofHash, a]));
160
+ assert.equal(byHash.get(good.proofHash)!.blockNumber, "24800448");
161
+ assert.equal(byHash.get(foreign.proofHash)!.blockNumber, undefined);
162
+ assert.equal(byHash.get(missing.proofHash)!.blockNumber, undefined);
163
+ assert.equal(result.findings.filter((f) => f.code === "anchor-title-unparseable").length, 2);
164
+ });
165
+ });
166
+
167
+ describe("audit anchors: non-anchor proofs are untouched", () => {
168
+ it("produces no records or findings for ordinary proofs and mutates nothing", async () => {
169
+ const a = await makeChainIdProof({ counter: "2", epochId: "E-PLAIN" });
170
+ const b = await makeChainIdProof({ counter: "4", epochId: "E-PLAIN" });
171
+ const ingest = await ingestAndVerify({
172
+ "proofs/a.json": proofJson(a.proof),
173
+ "proofs/b.json": proofJson(b.proof),
174
+ });
175
+ const statusesBefore = ingest.proofs.map((p) => p.verification?.status);
176
+
177
+ const result = identifyAnchors(ingest);
178
+
179
+ assert.equal(result.anchors.length, 0);
180
+ assert.equal(result.metadataOnlyProofHashes.length, 0);
181
+ assert.equal(result.findings.length, 0);
182
+ assert.deepEqual(
183
+ ingest.proofs.map((p) => p.verification?.status),
184
+ statusesBefore,
185
+ "identification is read-only"
186
+ );
187
+ });
188
+ });