@mikeargento/bitgraph-mcp 0.5.1 → 0.6.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 (56) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +1 -1
  3. package/dist/__tests__/carrier.test.d.ts +2 -0
  4. package/dist/__tests__/carrier.test.d.ts.map +1 -0
  5. package/dist/__tests__/carrier.test.js +243 -0
  6. package/dist/__tests__/carrier.test.js.map +1 -0
  7. package/dist/__tests__/encoding.test.js +1 -1
  8. package/dist/__tests__/encoding.test.js.map +1 -1
  9. package/dist/__tests__/format.test.js +6 -6
  10. package/dist/__tests__/format.test.js.map +1 -1
  11. package/dist/__tests__/scan.test.js +1 -1
  12. package/dist/__tests__/scan.test.js.map +1 -1
  13. package/dist/__tests__/tools.test.js +8 -7
  14. package/dist/__tests__/tools.test.js.map +1 -1
  15. package/dist/api.js +1 -1
  16. package/dist/api.js.map +1 -1
  17. package/dist/carrier-io.d.ts +52 -0
  18. package/dist/carrier-io.d.ts.map +1 -0
  19. package/dist/carrier-io.js +91 -0
  20. package/dist/carrier-io.js.map +1 -0
  21. package/dist/encoding.js +1 -1
  22. package/dist/encoding.js.map +1 -1
  23. package/dist/format.d.ts +17 -3
  24. package/dist/format.d.ts.map +1 -1
  25. package/dist/format.js +36 -15
  26. package/dist/format.js.map +1 -1
  27. package/dist/index.js +1 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/scan.d.ts +4 -0
  30. package/dist/scan.d.ts.map +1 -1
  31. package/dist/scan.js +44 -1
  32. package/dist/scan.js.map +1 -1
  33. package/dist/server.d.ts +1 -1
  34. package/dist/server.d.ts.map +1 -1
  35. package/dist/server.js +151 -29
  36. package/dist/server.js.map +1 -1
  37. package/dist/task.d.ts.map +1 -1
  38. package/dist/task.js +2 -2
  39. package/dist/task.js.map +1 -1
  40. package/dist/types.js +1 -1
  41. package/dist/types.js.map +1 -1
  42. package/package.json +13 -5
  43. package/src/__tests__/carrier.test.ts +271 -0
  44. package/src/__tests__/encoding.test.ts +1 -1
  45. package/src/__tests__/format.test.ts +6 -6
  46. package/src/__tests__/scan.test.ts +1 -1
  47. package/src/__tests__/tools.test.ts +8 -7
  48. package/src/api.ts +1 -1
  49. package/src/carrier-io.ts +120 -0
  50. package/src/encoding.ts +1 -1
  51. package/src/format.ts +56 -19
  52. package/src/index.ts +1 -1
  53. package/src/scan.ts +48 -1
  54. package/src/server.ts +169 -34
  55. package/src/task.ts +2 -2
  56. package/src/types.ts +1 -1
package/dist/scan.d.ts CHANGED
@@ -29,7 +29,11 @@ export interface ScannedFile {
29
29
  * then needs the bytes again.
30
30
  */
31
31
  state: Hash | null;
32
+ /** True when the bytes carry what looks like a C2PA manifest (Content Credentials). */
33
+ c2pa: boolean;
32
34
  }
35
+ /** One-shot form for bytes already in memory (a carrier's committed bytes). */
36
+ export declare function sniffC2paBytes(bytes: Uint8Array): boolean;
33
37
  /** Hash one file in a single pass. Throws, before any network call, when the path is not a regular file. */
34
38
  export declare function scanFile(path: string): Promise<ScannedFile>;
35
39
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAc,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAOpD,6FAA6F;AAC7F,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,aAAa,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,UAAU,CAAC;IACzB,SAAS,EAAE,aAAa,CAAC;IACzB;;;;OAIG;IACH,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;CACpB;AAKD,4GAA4G;AAC5G,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAoDjE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,GAAG,UAAU,CAMpF;AAED,MAAM,WAAW,SAAS;IACxB,uGAAuG;IACvG,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CA2C7F"}
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAc,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAOpD,6FAA6F;AAC7F,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,aAAa,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,UAAU,CAAC;IACzB,SAAS,EAAE,aAAa,CAAC;IACzB;;;;OAIG;IACH,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,uFAAuF;IACvF,IAAI,EAAE,OAAO,CAAC;CACf;AAuCD,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAEzD;AAKD,4GAA4G;AAC5G,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAuDjE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,GAAG,UAAU,CAMpF;AAED,MAAM,WAAW,SAAS;IACxB,uGAAuG;IACvG,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CA2C7F"}
package/dist/scan.js CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2024-2026 Mike Argento. Licensed under the MIT License. See LICENSE.
1
+ // Copyright (c) 2024-2026 Argento Computing Inc. Licensed under the MIT License. See LICENSE.
2
2
  /**
3
3
  * @mikeargento/bitgraph-mcp: the scan.
4
4
  *
@@ -19,6 +19,46 @@ import { readdir, stat } from "node:fs/promises";
19
19
  import { basename, join } from "node:path";
20
20
  import { placementForBytes } from "@mikeargento/bitgraph";
21
21
  import { getPlacement } from "@mikeargento/bitgraph-verify";
22
+ /**
23
+ * Content Credentials detection, structural and cheap: a C2PA manifest lives
24
+ * in a JUMBF box whose description ("jumd") is followed within a few bytes by
25
+ * the store label "c2pa", in every container (JPEG APP11, PNG caBX, ISO BMFF).
26
+ * Requiring the pair in order and near each other keeps a stray "c2pa" in
27
+ * prose from counting. Detection, not judgment: the proof page parses and
28
+ * displays the manifest itself.
29
+ */
30
+ const JUMD = Buffer.from("jumd", "ascii");
31
+ const C2PA_LABEL = Buffer.from("c2pa", "ascii");
32
+ const C2PA_NEAR = 96;
33
+ function windowHasC2pa(buf) {
34
+ for (let i = buf.indexOf(JUMD); i !== -1; i = buf.indexOf(JUMD, i + 1)) {
35
+ const from = i + JUMD.length;
36
+ const to = Math.min(buf.length, i + C2PA_NEAR);
37
+ if (buf.subarray(from, to).indexOf(C2PA_LABEL) !== -1)
38
+ return true;
39
+ }
40
+ return false;
41
+ }
42
+ class C2paSniffer {
43
+ found = false;
44
+ /** The last C2PA_NEAR + 4 bytes of the previous chunk, so a pair split across chunks is seen whole. */
45
+ carry = Buffer.alloc(0);
46
+ update(chunk) {
47
+ if (this.found)
48
+ return;
49
+ const buf = this.carry.length > 0 ? Buffer.concat([this.carry, chunk]) : chunk;
50
+ if (windowHasC2pa(buf)) {
51
+ this.found = true;
52
+ return;
53
+ }
54
+ const keep = Math.min(buf.length, C2PA_NEAR + JUMD.length);
55
+ this.carry = Buffer.from(buf.subarray(buf.length - keep));
56
+ }
57
+ }
58
+ /** One-shot form for bytes already in memory (a carrier's committed bytes). */
59
+ export function sniffC2paBytes(bytes) {
60
+ return windowHasC2pa(Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength));
61
+ }
22
62
  /** Bytes the placement decision reads: every magic number sits in the first 16. */
23
63
  const SNIFF = 64;
24
64
  /** Hash one file in a single pass. Throws, before any network call, when the path is not a regular file. */
@@ -40,10 +80,12 @@ export async function scanFile(path) {
40
80
  if (prefix !== null && prefix.length > 0)
41
81
  run.fused = createHash("sha256").update(prefix);
42
82
  };
83
+ const sniffer = new C2paSniffer();
43
84
  const feed = (chunk) => {
44
85
  origin.update(chunk);
45
86
  if (run.fused !== null)
46
87
  run.fused.update(chunk);
88
+ sniffer.update(chunk);
47
89
  };
48
90
  for await (const raw of createReadStream(path)) {
49
91
  const chunk = raw;
@@ -78,6 +120,7 @@ export async function scanFile(path) {
78
120
  originDigest: Uint8Array.from(digest),
79
121
  placement,
80
122
  state,
123
+ c2pa: sniffer.found,
81
124
  };
82
125
  }
83
126
  /**
package/dist/scan.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"scan.js","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAE9F;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAa,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAuB5D;;;;;;;GAOG;AACH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC1C,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAChD,MAAM,SAAS,GAAG,EAAE,CAAC;AAErB,SAAS,aAAa,CAAC,GAAW;IAChC,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACvE,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QAC7B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC;QAC/C,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;IACrE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,WAAW;IACf,KAAK,GAAG,KAAK,CAAC;IACd,uGAAuG;IAC/F,KAAK,GAAW,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,CAAC,KAAa;QAClB,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC/E,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,OAAO;QACT,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3D,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IAC5D,CAAC;CACF;AAED,+EAA+E;AAC/E,MAAM,UAAU,cAAc,CAAC,KAAiB;IAC9C,OAAO,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;AACtF,CAAC;AAED,mFAAmF;AACnF,MAAM,KAAK,GAAG,EAAE,CAAC;AAEjB,4GAA4G;AAC5G,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY;IACzC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC;IACnE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACvB,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACpC,uDAAuD;IACvD,MAAM,GAAG,GAA4D,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACtG,IAAI,IAAI,GAAkB,IAAI,CAAC;IAC/B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,KAAK,GAAG,CAAC,CAAgB,EAAE,EAAE;QACjC,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC;QAClB,oEAAoE;QACpE,gFAAgF;QAChF,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;QAC3D,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,GAAG,CAAC,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5F,CAAC,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,CAAC,KAAa,EAAE,EAAE;QAC7B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACrB,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI;YAAE,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAChD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC,CAAC;IACF,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,GAAa,CAAC;QAC5B,IAAI,GAAG,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC3B,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;YAC5D,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;gBACzB,KAAK,CAAC,iBAAiB,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBAClE,IAAI,CAAC,IAAI,CAAC,CAAC;gBACX,IAAI,GAAG,IAAI,CAAC;YACd,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;QACD,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC;IACxB,CAAC;IACD,IAAI,GAAG,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;QAC3B,yDAAyD;QACzD,KAAK,CAAC,iBAAiB,CAAC,IAAI,UAAU,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClE,IAAI,IAAI,KAAK,IAAI;YAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IACD,MAAM,SAAS,GAAG,GAAG,CAAC,SAA0B,CAAC;IACjD,MAAM,KAAK,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC;IAC5D,yFAAyF;IACzF,MAAM,MAAM,GAAG,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;IAC3E,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;QACpB,IAAI;QACJ,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACpC,YAAY,EAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;QACrC,SAAS;QACT,KAAK;QACL,IAAI,EAAE,OAAO,CAAC,KAAK;KACpB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,IAAiB,EAAE,UAAsB;IACtE,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,sCAAsC,CAAC,CAAC;IAC7F,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC/C,IAAI,SAAS,EAAE,KAAK,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,aAAa,IAAI,CAAC,SAAS,eAAe,CAAC,CAAC;IAChG,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC;IAC7G,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AACpE,CAAC;AASD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAAwB,EAAE,KAAa;IACvE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,EAAE;QACzB,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,OAAO;QACxB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACZ,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACd,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,aAAa,KAAK,kCAAkC,CAAC,CAAC;IAClG,CAAC,CAAC;IACF,MAAM,IAAI,GAAG,KAAK,EAAE,GAAW,EAAiB,EAAE;QAChD,MAAM,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;aAC1D,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;aACtC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACpE,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,CAAC,CAAC,cAAc,EAAE;gBAAE,SAAS;YACjC,IAAI,CAAC,CAAC,WAAW,EAAE;gBAAE,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;iBACjC,IAAI,CAAC,CAAC,MAAM,EAAE;gBAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;YAC3B,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;gBACvB,WAAW,IAAI,CAAC,CAAC;gBACjB,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;YAChB,CAAC;iBAAM,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;gBACzB,IAAI,CAAC,CAAC,CAAC,CAAC;YACV,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,IAAI,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC;gBAAE,MAAM,GAAG,CAAC;YAChD,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,kBAAkB,QAAQ,CAAC,MAAM,sCAAsC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;IAC9K,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;AAChC,CAAC"}
package/dist/server.d.ts CHANGED
@@ -17,7 +17,7 @@ import { type FuseSetProgress } from "@mikeargento/bitgraph";
17
17
  import { type ApiConfig } from "./api.js";
18
18
  import { type ScannedFile } from "./scan.js";
19
19
  import type { BitGraphProof } from "./types.js";
20
- export declare const SERVER_VERSION = "0.5.1";
20
+ export declare const SERVER_VERSION = "0.6.0";
21
21
  /** Files one call may BitGraph after directories expand: one set. The site's own ceiling for a set/2. */
22
22
  export declare const MAX_MEMBERS = 100000;
23
23
  /** Rows the structured result lists in full; every fused row shares the set's position. */
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,OAAO,EAA4F,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACvJ,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,UAAU,CAAC;AAmBlB,OAAO,EAAyC,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAEpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,eAAO,MAAM,cAAc,UAAU,CAAC;AAKtC,yGAAyG;AACzG,eAAO,MAAM,WAAW,SAAU,CAAC;AAOnC,2FAA2F;AAC3F,eAAO,MAAM,OAAO,MAAM,CAAC;AAC3B,qEAAqE;AACrE,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,mFAAmF;AACnF,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;IACvB,KAAK,EAAE,aAAa,CAAC;IACrB,wDAAwD;IACxD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,yCAAyC;IACzC,OAAO,EAAE,KAAK,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,mDAAmD;QACnD,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,CAAC;QACxB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,+DAA+D;QAC/D,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,CAAC,CAAC;CACJ;AACD,MAAM,MAAM,SAAS,GAAG,CACtB,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE;IAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,IAAI,CAAA;CAAE,KACxE,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzB,8CAA8C;AAC9C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;CACzB;AACD,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAEzF,MAAM,WAAW,UAAU;IACzB,oHAAoH;IACpH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,yHAAyH;IACzH,QAAQ,CAAC,EAAE,UAAU,CAAC;CACvB;AA4KD,2EAA2E;AAC3E,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAmCD,wBAAgB,WAAW,CAAC,IAAI,GAAE,UAAe,GAAG,SAAS,CA+hB5D"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,OAAO,EAA4F,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACvJ,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,UAAU,CAAC;AAmBlB,OAAO,EAAyD,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAIpG,OAAO,KAAK,EAAE,aAAa,EAAuB,MAAM,YAAY,CAAC;AAErE,eAAO,MAAM,cAAc,UAAU,CAAC;AAKtC,yGAAyG;AACzG,eAAO,MAAM,WAAW,SAAU,CAAC;AAOnC,2FAA2F;AAC3F,eAAO,MAAM,OAAO,MAAM,CAAC;AAC3B,qEAAqE;AACrE,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,mFAAmF;AACnF,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;IACvB,KAAK,EAAE,aAAa,CAAC;IACrB,wDAAwD;IACxD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,yCAAyC;IACzC,OAAO,EAAE,KAAK,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,mDAAmD;QACnD,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,CAAC;QACxB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,+DAA+D;QAC/D,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,CAAC,CAAC;CACJ;AACD,MAAM,MAAM,SAAS,GAAG,CACtB,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE;IAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,IAAI,CAAA;CAAE,KACxE,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzB,8CAA8C;AAC9C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;CACzB;AACD,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAEzF,MAAM,WAAW,UAAU;IACzB,oHAAoH;IACpH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,yHAAyH;IACzH,QAAQ,CAAC,EAAE,UAAU,CAAC;CACvB;AAyND,2EAA2E;AAC3E,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAmCD,wBAAgB,WAAW,CAAC,IAAI,GAAE,UAAe,GAAG,SAAS,CAunB5D"}
package/dist/server.js CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2024-2026 Mike Argento. Licensed under the MIT License. See LICENSE.
1
+ // Copyright (c) 2024-2026 Argento Computing Inc. Licensed under the MIT License. See LICENSE.
2
2
  /**
3
3
  * @mikeargento/bitgraph-mcp: tool definitions.
4
4
  *
@@ -20,9 +20,11 @@ import { FuseError, MAX_SET_MEMBERS, builderFor, fuse, fuseSet, fusedNamesFor }
20
20
  import { ApiError, batchCheck, configFromEnv, getProofDetail, indexSetMembers, search, } from "./api.js";
21
21
  import { fromUrlSafeB64, looksLikeDigest, mapConcurrent, sha256FileB64, toUrlSafeB64, } from "./encoding.js";
22
22
  import { capJson, positionOf, proofUrl, renderCheckMarkdown, renderProofMarkdown, renderRecordMarkdown, } from "./format.js";
23
- import { expandPaths, fusedDigestFor, scanFile } from "./scan.js";
23
+ import { expandPaths, fusedDigestFor, scanFile, sniffC2paBytes } from "./scan.js";
24
+ import { readCarrierFile, sniffCarrierTail } from "./carrier-io.js";
25
+ import { stat } from "node:fs/promises";
24
26
  import { SLOT_TTL_SECONDS, TASK_INSTRUCTIONS, beginTask, decodeTaskToken, sealTask, writeProofBeside } from "./task.js";
25
- export const SERVER_VERSION = "0.5.1";
27
+ export const SERVER_VERSION = "0.6.0";
26
28
  const SCAN_CONCURRENCY = 4;
27
29
  /** Paths per call; a directory counts once and expands to its files. */
28
30
  const MAX_PATHS = 2000;
@@ -100,6 +102,29 @@ async function fuseSetDefault(files, config, opts) {
100
102
  })),
101
103
  };
102
104
  }
105
+ async function classifyPath(p) {
106
+ const info = await stat(p);
107
+ if (info.isFile() && (await sniffCarrierTail(p, info.size))) {
108
+ if (info.size > MAX_LOADED_BYTES) {
109
+ return {
110
+ kind: "carrier", path: p, status: "too-large", innerDigestB64: null, view: null, proof: null, c2pa: false,
111
+ reason: `a BitGraphed file larger than this tool loads (${Math.round(MAX_LOADED_BYTES / (1024 * 1024))} MiB); judge it offline with: npx @mikeargento/bitgraph-audit "${p}"`,
112
+ };
113
+ }
114
+ const r = await readCarrierFile(p);
115
+ if (r.status === "corrupt" || r.innerDigestB64 === null) {
116
+ return {
117
+ kind: "carrier", path: p, status: "corrupt", innerDigestB64: null, view: null, proof: null, c2pa: false,
118
+ reason: `a carrier block was found at the end of the file but is unreadable: ${r.corruptReason ?? "unspecified"}. A corrupted block, not a forgery; nothing was minted for it.`,
119
+ };
120
+ }
121
+ return {
122
+ kind: "carrier", path: p, status: "ok", innerDigestB64: r.innerDigestB64, view: r.view,
123
+ proof: r.result?.payload?.proof ?? null, c2pa: r.inner !== null && sniffC2paBytes(r.inner), reason: null,
124
+ };
125
+ }
126
+ return { kind: "plain", file: await scanFile(p) };
127
+ }
103
128
  /** Hash the given paths (bounded concurrency). Throws before any network call. */
104
129
  async function hashPaths(paths) {
105
130
  const failures = [];
@@ -215,17 +240,19 @@ export function buildServer(deps = {}) {
215
240
  name: "bitgraph-mcp-server",
216
241
  version: SERVER_VERSION,
217
242
  }, {
218
- instructions: "BitGraph gives a file's bytes a causal position in a public ledger bracketed by Ethereum anchors. bitgraph_record makes ONE BitGraph of everything in a call, files and folders alike: a single file is fused on its own; two or more become one set under one slot, one position, every file's new fused bytes listed by digest in the committed artifact. " +
243
+ instructions: "BitGraph gives a file's bytes a causal position in a public sequence bracketed by Ethereum anchors. bitgraph_record makes ONE BitGraph of everything in a call, files and folders alike: a single file is fused on its own; two or more become one set under one slot, one position, every file's new fused bytes listed by digest in the committed artifact. " +
219
244
  "Files are read on this machine and never uploaded or modified; the new bytes are virtual and never written. Recordings are permanent: only make BitGraphs of files the user asked for, and never generate content just to record it. bitgraph_check and bitgraph_get_proof are read-only. " +
245
+ "A BitGraphed file (one that carries its own proof, bitgraph-carrier/1) is recognized by its structure: bitgraph_check judges it offline from the proof inside and states the window, and bitgraph_record never re-mints it, because the envelope is not the recorded thing, the bytes inside are. " +
220
246
  "To do work INSIDE a BitGraph, call bitgraph_open BEFORE starting: it returns a position and its commitment; put the commitment string into the task, seal the task with bitgraph_commit within 120 seconds, then record the outputs with bitgraph_record. The task then could not have existed before the position's floor block, and the outputs sit after it.",
221
247
  });
222
248
  server.registerTool("bitgraph_record", {
223
249
  title: "Make a BitGraph",
224
- description: "Make a BitGraph of files or folders. Everything in one call becomes ONE BitGraph, the way a drop on the site works: a single file is fused on its own slot; two or more files become a set under a single slot in the BitGraph ledger (bitgraph.ing), one position for all of them. " +
250
+ description: "Make a BitGraph of files or folders. Everything in one call becomes ONE BitGraph, the way a drop on the site works: a single file is fused on its own slot; two or more files become a set under a single slot on BitGraph (bitgraph.ing), one position for all of them. " +
225
251
  "On this machine each file is read once for its SHA-256 (the origin) and a hasher state; an unused slot is allocated before any new file exists; every file's new fused bytes (the original plus a registered placement carrying the slot's commitment: a 48-byte trailer for JPEG, PNG, GIF, TIFF and TIFF-based raws, BMP, WebP, WAV and AVI, a small tar container with the original first for everything else) are hashed from that state without being written or held; and for a set the canonical list of those digests (above 2,000 files, a Merkle root over it) is committed under the same slot. " +
226
252
  "Files are never modified and never uploaded: only digests, the committed artifact and slot records leave the machine. " +
227
253
  "Give file paths, directory paths, or both (absolute paths preferred): a directory is every regular file under it, recursively, with hidden entries and symbolic links left out. " +
228
- "Files BitGraph still indexes (recorded before 2026-09-08) are NOT made again by default; they come back as 'on record' with their earliest position. BitGraph does not index new proofs, so a file may already have a BitGraph its holder keeps. Pass again=true to make a new BitGraph regardless. " +
254
+ "Files already on record are NOT made again by default; they come back as 'on record' with their earliest position. A file can also hold a BitGraph its holder keeps, which no lookup sees. Pass again=true to make a new BitGraph regardless. " +
255
+ "A BitGraphed file (bitgraph-carrier/1, a file that carries its own proof) is never minted, with or without again: its carried proof is judged offline and reported, because the envelope is not the recorded thing, the bytes inside are. " +
229
256
  "Positions are permanent and the proof comes back to you to keep, so only BitGraph files the user asked to, and never generate content just to record it. " +
230
257
  "Returns one outcome per file: 'fused' (for a set, its row, one of N, and the set's position and proof page; for a single file, its own position and Frame), 'on record', or 'not fused' (with the reason). Keep the proof beside the files; BitGraph does not index it. " +
231
258
  "Use bitgraph_check instead when the user only wants to know whether files are on record.",
@@ -238,7 +265,7 @@ export function buildServer(deps = {}) {
238
265
  again: z
239
266
  .boolean()
240
267
  .default(false)
241
- .describe("false (default): files BitGraph still indexes are returned as-is, nothing made. true: put every file in the set regardless. Outcomes are per unique file content: two paths with identical bytes are one member."),
268
+ .describe("false (default): files already on record are returned as-is, nothing made. true: put every file in the set regardless. Outcomes are per unique file content: two paths with identical bytes are one member."),
242
269
  response_format: responseFormatSchema,
243
270
  },
244
271
  annotations: {
@@ -264,15 +291,18 @@ export function buildServer(deps = {}) {
264
291
  return fail(`Error: ${flushed.pending} members of an earlier set are still waiting to be indexed and the site could not take them; nothing was BitGraphed. Run bitgraph_record again in a moment: the waiting evidence is sent first.`);
265
292
  }
266
293
  }
267
- // 2. The scan: one pass per file.
294
+ // 2. The scan: one pass per file. A BitGraphed file is set aside here,
295
+ // judged from the proof it carries, and never enters the mint below.
268
296
  let scanned = 0;
269
297
  report(0, files.length, `hashing ${files.length} files`);
270
- const scans = await mapConcurrent(files, SCAN_CONCURRENCY, async (p) => {
271
- const s = await scanFile(p);
298
+ const classified = await mapConcurrent(files, SCAN_CONCURRENCY, async (p) => {
299
+ const c = await classifyPath(p);
272
300
  scanned += 1;
273
301
  report(scanned, files.length, `hashed ${scanned} of ${files.length}`);
274
- return s;
302
+ return c;
275
303
  });
304
+ const scans = classified.filter((c) => c.kind === "plain").map((c) => c.file);
305
+ const carriers = classified.filter((c) => c.kind === "carrier");
276
306
  // 3. Unique by content; the first path names the member, every path is reported.
277
307
  const byDigest = new Map();
278
308
  for (const s of scans) {
@@ -281,15 +311,24 @@ export function buildServer(deps = {}) {
281
311
  byDigest.set(s.digestB64, entry);
282
312
  }
283
313
  const unique = [...byDigest.keys()];
284
- // 4. What is on record already.
285
- report(0, 1, "checking the ledger");
286
- const checked = await batchCheck(config, unique.map(toUrlSafeB64));
314
+ // 4. What is on record already. A BitGraphed file is looked up by the
315
+ // digest of its committed bytes, never by the envelope's.
316
+ report(0, 1, "checking BitGraph's copy");
317
+ const carrierInner = [...new Set(carriers.filter((c) => c.innerDigestB64 !== null).map((c) => c.innerDigestB64))];
318
+ const lookups = [...new Set([...unique, ...carrierInner])];
319
+ const checked = lookups.length > 0 ? await batchCheck(config, lookups.map(toUrlSafeB64)) : { results: {} };
287
320
  const existing = new Map();
288
321
  for (const d of unique) {
289
322
  const entry = checked.results[toUrlSafeB64(d)];
290
323
  if (entry && entry.proofs.length > 0)
291
324
  existing.set(d, entry.proofs);
292
325
  }
326
+ const carrierLedger = new Map();
327
+ for (const d of carrierInner) {
328
+ const entry = checked.results[toUrlSafeB64(d)];
329
+ if (entry && entry.proofs.length > 0)
330
+ carrierLedger.set(d, entry.proofs);
331
+ }
293
332
  // 5. The set: every fresh file (every file, with again), one call.
294
333
  const excluded = new Map();
295
334
  const toMint = [];
@@ -366,7 +405,7 @@ export function buildServer(deps = {}) {
366
405
  const m = memberOf.get(digest);
367
406
  const prior = existing.get(digest);
368
407
  for (const path of entry.paths) {
369
- const base = { path, digest: toUrlSafeB64(digest) };
408
+ const base = { path, digest: toUrlSafeB64(digest), ...(entry.file.c2pa ? { c2pa: true } : {}) };
370
409
  if (solo !== null && solo.file.digestB64 === digest) {
371
410
  const { counter, epoch } = positionOf(solo.proof);
372
411
  outcomes.push({
@@ -430,8 +469,37 @@ export function buildServer(deps = {}) {
430
469
  }
431
470
  }
432
471
  }
472
+ // 7b. BitGraphed files: judged from the proof inside, never minted.
473
+ for (const c of carriers) {
474
+ if (c.status !== "ok" || c.innerDigestB64 === null) {
475
+ outcomes.push({
476
+ path: c.path, digest: "", outcome: "not fused", artifact_digest: null, placement: null,
477
+ counter: null, epoch: null, member: null, member_count: null, total_positions: 0, proof_url: null,
478
+ error: c.reason ?? "unreadable BitGraphed file",
479
+ });
480
+ continue;
481
+ }
482
+ const inner = c.innerDigestB64;
483
+ const rows = carrierLedger.get(inner);
484
+ const extras = { ...(c.view ? { carrier: c.view } : {}), ...(c.c2pa ? { c2pa: true } : {}) };
485
+ if (rows !== undefined && rows.length > 0) {
486
+ const first = rows[0];
487
+ const { counter, epoch } = positionOf(first.proof);
488
+ outcomes.push({
489
+ path: c.path, digest: toUrlSafeB64(inner), outcome: "on record", artifact_digest: null, placement: null,
490
+ counter, epoch, member: first.member ? first.member.index + 1 : null, member_count: first.member ? first.member.count : null,
491
+ total_positions: rows.length, proof_url: proofUrl(config.baseUrl, inner), ...extras,
492
+ });
493
+ }
494
+ else {
495
+ outcomes.push({
496
+ path: c.path, digest: toUrlSafeB64(inner), outcome: "carried", artifact_digest: null, placement: null,
497
+ counter: null, epoch: null, member: null, member_count: null, total_positions: 0, proof_url: null, ...extras,
498
+ });
499
+ }
500
+ }
433
501
  // Rows that need reading come first, so a cap drops fused rows, which all share one position.
434
- const order = { "not fused": 0, "on record": 1, fused: 2 };
502
+ const order = { "not fused": 0, "on record": 1, carried: 2, fused: 3 };
435
503
  outcomes.sort((a, b) => order[a.outcome] - order[b.outcome]);
436
504
  const listed = outcomes.slice(0, ROW_CAP);
437
505
  const omitted = outcomes.length - listed.length;
@@ -440,6 +508,7 @@ export function buildServer(deps = {}) {
440
508
  directories: expanded.directories,
441
509
  fused: outcomes.filter((o) => o.outcome === "fused").length,
442
510
  on_record: outcomes.filter((o) => o.outcome === "on record").length,
511
+ carried: outcomes.filter((o) => o.outcome === "carried").length,
443
512
  not_fused: outcomes.filter((o) => o.outcome === "not fused").length,
444
513
  };
445
514
  const structured = {
@@ -531,7 +600,7 @@ export function buildServer(deps = {}) {
531
600
  }
532
601
  }
533
602
  else {
534
- proofNote = "Save the JSON below as a file beside the task bytes, whole and unedited, every field: a proof missing slotAllocation or environment cannot be verified. ";
603
+ proofNote = "Save the JSON below as a file beside the task bytes, whole and unedited, every field, including environment.attestation.reportB64, the long base64 string, copied exactly: a proof missing slotAllocation, environment, or the attestation cannot be verified. ";
535
604
  }
536
605
  const structured = { outcome: "sealed", slot_counter: state.slot.counter, counter, epoch: epoch ? toUrlSafeB64(epoch) : null, floor_block: floor, artifact_digest: toUrlSafeB64(sealed.artifactDigestB64), commitment_offsets: sealed.offsets, proof_path: proofPath, instructions: proofNote.trim(), proof: sealed.proof };
537
606
  if (response_format === "json")
@@ -548,9 +617,10 @@ export function buildServer(deps = {}) {
548
617
  });
549
618
  server.registerTool("bitgraph_check", {
550
619
  title: "Check for BitGraphs",
551
- description: "Check whether files or digests are on record in the BitGraph ledger, without recording anything. " +
620
+ description: "Check whether files or digests are on record in BitGraph's copy, without recording anything. " +
552
621
  `Accepts file paths and directory paths (every regular file under them, up to ${MAX_CHECK_FILES} in all; hashed locally, only digests are sent) and/or raw SHA-256 digests in standard or URL-safe base64. ` +
553
622
  "Returns, per item: on_record (the bytes are on record, as an exact recording, as the original a new file was made from, or as a member of a set), every position by counter, and the proof page URL. " +
623
+ "A BitGraphed file (bitgraph-carrier/1) is recognized by structure: the proof it carries is verified OFFLINE here (signatures, floor identity, block-header witnesses) and its verdict and window are reported, and the lookup uses the committed bytes inside, never the envelope. " +
554
624
  "Read-only. Use bitgraph_record to BitGraph files that turn out not to be on record.",
555
625
  inputSchema: {
556
626
  paths: z
@@ -577,8 +647,19 @@ export function buildServer(deps = {}) {
577
647
  const inputs = [];
578
648
  if (paths && paths.length > 0) {
579
649
  const { files } = await expandPaths(paths, MAX_CHECK_FILES);
580
- const hashed = await hashPaths(files);
581
- hashed.forEach((d, i) => inputs.push({ label: files[i], standardDigest: d }));
650
+ const classified = await mapConcurrent(files, SCAN_CONCURRENCY, classifyPath);
651
+ classified.forEach((c, i) => {
652
+ const label = files[i];
653
+ if (c.kind === "plain") {
654
+ inputs.push({ label, standardDigest: c.file.digestB64, ...(c.file.c2pa ? { c2pa: true } : {}) });
655
+ }
656
+ else if (c.status === "ok" && c.innerDigestB64 !== null) {
657
+ inputs.push({ label, standardDigest: c.innerDigestB64, ...(c.view ? { carrier: c.view } : {}), ...(c.c2pa ? { c2pa: true } : {}) });
658
+ }
659
+ else {
660
+ inputs.push({ label, standardDigest: "", note: c.reason ?? "unreadable BitGraphed file" });
661
+ }
662
+ });
582
663
  }
583
664
  for (const d of digests ?? []) {
584
665
  const trimmed = d.trim();
@@ -593,19 +674,23 @@ export function buildServer(deps = {}) {
593
674
  if (inputs.length > MAX_CHECK_FILES) {
594
675
  return fail(`Error: ${inputs.length} items; check at most ${MAX_CHECK_FILES} at a time.`);
595
676
  }
596
- const checked = await batchCheck(config, inputs.map((i) => toUrlSafeB64(i.standardDigest)));
677
+ const lookable = [...new Set(inputs.filter((i) => i.standardDigest !== "").map((i) => toUrlSafeB64(i.standardDigest)))];
678
+ const checked = lookable.length > 0 ? await batchCheck(config, lookable) : { results: {} };
597
679
  const outcomes = inputs.map((input) => {
598
- const entry = checked.results[toUrlSafeB64(input.standardDigest)];
680
+ const entry = input.standardDigest === "" ? undefined : checked.results[toUrlSafeB64(input.standardDigest)];
599
681
  const proofs = entry?.proofs ?? [];
600
682
  const positions = proofs.map((p) => ({ ...positionOf(p.proof), ...(p.member ? { member: p.member } : {}) }));
601
683
  return {
602
684
  input: input.label,
603
- digest: toUrlSafeB64(input.standardDigest),
685
+ digest: input.standardDigest === "" ? "" : toUrlSafeB64(input.standardDigest),
604
686
  // A fused descendant that names these bytes as origin is not a recording of them.
605
687
  // The original and the new file made from it find the same proof.
606
688
  on_record: proofs.length > 0,
607
689
  positions,
608
690
  proof_url: proofs.length > 0 ? proofUrl(config.baseUrl, input.standardDigest) : null,
691
+ ...(input.carrier ? { carrier: input.carrier } : {}),
692
+ ...(input.c2pa ? { c2pa: true } : {}),
693
+ ...(input.note !== undefined ? { note: input.note } : {}),
609
694
  };
610
695
  });
611
696
  const structured = {
@@ -625,7 +710,8 @@ export function buildServer(deps = {}) {
625
710
  server.registerTool("bitgraph_get_proof", {
626
711
  title: "Get a BitGraph proof",
627
712
  description: "Fetch a BitGraph proof and its context: causal position, every position the same bytes occupy, the row a set member holds (one of N), and the two-sided Ethereum anchor window " +
628
- "('BitGraphed between X and Y'). Look up by digest (base64, either form), by BitGraph number (e.g. '4523' or '#4,523', current epoch), or by file path (hashed locally). " +
713
+ "(after the floor block's time; before the ANCHORING of the later block, a position bound, never that block's mine time). Look up by digest (base64, either form), by BitGraph number (e.g. '4523' or '#4,523', current epoch), or by file path (hashed locally). " +
714
+ "A path to a BitGraphed file (bitgraph-carrier/1) looks up the committed bytes inside it, and when the ledger has no row the proof the file carries answers offline. " +
629
715
  "Exactly one of digest, number, or path is required. Read-only. " +
630
716
  "markdown returns a summary; json returns the full proof object with positions and anchor window.",
631
717
  inputSchema: {
@@ -667,6 +753,7 @@ export function buildServer(deps = {}) {
667
753
  }
668
754
  let urlSafeDigest;
669
755
  let selCounter = counter;
756
+ let carrierRow = null;
670
757
  if (number !== undefined) {
671
758
  const result = await search(config, number);
672
759
  if (!result.found || result.digest === undefined) {
@@ -677,8 +764,17 @@ export function buildServer(deps = {}) {
677
764
  selCounter = result.counter;
678
765
  }
679
766
  else if (path !== undefined) {
680
- const hashed = await hashPaths([path]);
681
- urlSafeDigest = toUrlSafeB64(hashed[0]);
767
+ const c = await classifyPath(path);
768
+ if (c.kind === "carrier") {
769
+ if (c.status !== "ok" || c.innerDigestB64 === null) {
770
+ return fail(`Error: ${c.reason ?? "unreadable BitGraphed file"}`);
771
+ }
772
+ carrierRow = c;
773
+ urlSafeDigest = toUrlSafeB64(c.innerDigestB64);
774
+ }
775
+ else {
776
+ urlSafeDigest = toUrlSafeB64(c.file.digestB64);
777
+ }
682
778
  }
683
779
  else {
684
780
  const trimmed = digest.trim();
@@ -691,13 +787,39 @@ export function buildServer(deps = {}) {
691
787
  const selEpoch = epoch !== undefined ? toUrlSafeB64(fromUrlSafeB64(epoch)) : undefined;
692
788
  const detail = await getProofDetail(config, urlSafeDigest, selCounter, selEpoch);
693
789
  if (detail.proofs.length === 0) {
790
+ if (carrierRow !== null && carrierRow.view !== null && carrierRow.proof !== null) {
791
+ // The ledger has no row, and the file answers for itself.
792
+ const v = carrierRow.view;
793
+ const synth = {
794
+ proofs: [{ proof: carrierRow.proof }],
795
+ positions: [],
796
+ causalWindow: {
797
+ anchorBefore: v.not_before
798
+ ? { blockNumber: v.not_before.block, blockHash: v.not_before.hash, etherscanUrl: `https://etherscan.io/block/${v.not_before.block}`, blockTime: v.not_before.time }
799
+ : null,
800
+ anchorAfter: v.not_after
801
+ ? { blockNumber: v.not_after.block, blockHash: v.not_after.hash, etherscanUrl: `https://etherscan.io/block/${v.not_after.block}`, blockTime: v.not_after.time }
802
+ : null,
803
+ },
804
+ };
805
+ const offlineNote = `Judged offline from the proof this BitGraphed file carries: carried proof ${v.verdict}${v.not_after === null ? ", closing anchor NOT FETCHED" : ""}. Not found in this ledger.`;
806
+ const structured = { ...synth, carrier: v };
807
+ if (response_format === "json")
808
+ return ok(capJson(structured).text, structured);
809
+ return ok(`${renderProofMarkdown(synth, config.baseUrl)}\n\n${offlineNote}`, structured);
810
+ }
694
811
  return fail(`Not on record: no proof exists for digest ${urlSafeDigest}. Use bitgraph_record to make a BitGraph of the file.`);
695
812
  }
696
813
  if (response_format === "json") {
697
- const capped = capJson(detail);
698
- return ok(capped.text, detail);
814
+ const withCarrier = carrierRow?.view ? { ...detail, carrier: carrierRow.view } : detail;
815
+ const capped = capJson(withCarrier);
816
+ return ok(capped.text, withCarrier);
699
817
  }
700
- return ok(renderProofMarkdown(detail, config.baseUrl), detail);
818
+ const md = renderProofMarkdown(detail, config.baseUrl);
819
+ const carrierNote = carrierRow?.view
820
+ ? `\n\nThis file carries its own proof (carried proof ${carrierRow.view.verdict}, judged offline).`
821
+ : "";
822
+ return ok(`${md}${carrierNote}`, detail);
701
823
  }
702
824
  catch (err) {
703
825
  return fail(errorText(err));