@zanii/monitor 0.1.0 → 0.2.1

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/README.md CHANGED
@@ -54,6 +54,7 @@ await monitor('https://ledger.zanii.agency', {
54
54
 
55
55
  ## Changelog
56
56
 
57
+ - **0.2.1** — now depends on `@zanii/core` 0.5 (protocol 1.2), so it shares one core with `@zanii/sdk` 0.5 instead of pulling in a second, older copy. No API change.
57
58
  - **0.1.0** — initial release: `checkOnce`, `monitor`, and the `zanii-monitor` CLI.
58
59
 
59
60
  ## License
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEjF,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,qEAAqE;IACrE,KAAK,EAAE,YAAY,CAAC;CACrB;AAQD;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,YAAY,EACnB,SAAS,GAAE,SAAiC,GAC3C,OAAO,CAAC,aAAa,CAAC,CAoDxB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1D;AAED,8EAA8E;AAC9E,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,KAAK,CAAC,CAevF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEjF,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,qEAAqE;IACrE,KAAK,EAAE,YAAY,CAAC;CACrB;AAQD;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,YAAY,EACnB,SAAS,GAAE,SAAiC,GAC3C,OAAO,CAAC,aAAa,CAAC,CAkExB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1D;AAED,8EAA8E;AAC9E,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,KAAK,CAAC,CAevF"}
package/dist/index.js CHANGED
@@ -31,12 +31,25 @@ export async function checkOnce(server, prev, fetchImpl = (u, i) => fetch(u, i))
31
31
  const checks = [];
32
32
  const violations = [];
33
33
  const push = (name, ok, detail) => {
34
- checks.push({ name, ok, detail });
34
+ checks.push(ok ? { name, ok } : { name, ok, detail });
35
35
  if (!ok)
36
36
  violations.push(detail ?? name);
37
37
  };
38
38
  const sth = (await get('/v1/sth'));
39
39
  push('signed tree head signature', verifySTH(sth) && !!sth.log_id, 'STH signature invalid');
40
+ // Prove `firstRoot@first` is an append-only prefix of the snapshot head. The proof is
41
+ // PINNED to the snapshot size (SPEC: ?first=m&second=n), so writes landing during the
42
+ // run cannot race the comparison into a false "history rewritten" alarm (TOCTOU).
43
+ // A pre-`second` server ignores the pin and proves against its live tree; we then
44
+ // verify against the (size, root) it actually proved — still catches any rewrite of
45
+ // history up to `first`, at the cost of trusting the served head for the newest tail.
46
+ const verifyExtension = async (first, firstRoot) => {
47
+ if (first === 0)
48
+ return true; // the empty tree is trivially a prefix
49
+ const c = await get(`/v1/consistency?first=${first}&second=${sth.size}`);
50
+ const pinned = c.second === sth.size;
51
+ return verifyConsistency(first, pinned ? sth.size : c.second, toRoot(firstRoot), toRoot(pinned ? sth.root : c.second_root), c.proof.map(hexToBytes));
52
+ };
40
53
  if (prev) {
41
54
  if (sth.size < prev.size) {
42
55
  push(`append-only (size ${sth.size} ≥ ${prev.size})`, false, `log SHRANK: size ${sth.size} < previously seen ${prev.size}`);
@@ -45,20 +58,24 @@ export async function checkOnce(server, prev, fetchImpl = (u, i) => fetch(u, i))
45
58
  push('append-only (root unchanged at same size)', sth.root === prev.root, `root changed at unchanged size ${sth.size} — history was rewritten`);
46
59
  }
47
60
  else {
48
- const c = await get(`/v1/consistency?first=${prev.size}`);
49
- const ok = verifyConsistency(prev.size, sth.size, toRoot(prev.root), toRoot(sth.root), c.proof.map(hexToBytes));
50
- push(`append-only (consistency ${prev.size}→${sth.size})`, ok, `consistency proof ${prev.size}→${sth.size} FAILED — history was rewritten`);
61
+ push(`append-only (consistency ${prev.size}→${sth.size})`, await verifyExtension(prev.size, prev.root), `consistency proof ${prev.size}→${sth.size} FAILED — history was rewritten`);
51
62
  }
52
63
  }
53
64
  // Every anchored checkpoint must be an append-only prefix of the current tree.
54
65
  try {
55
66
  const { anchors } = (await get('/v1/anchors'));
56
67
  for (const a of anchors ?? []) {
57
- let ok = verifySTH(a.sth) && a.sth.log_id === sth.log_id && a.sth.size <= sth.size;
58
- if (ok && a.sth.size < sth.size) {
59
- const c = await get(`/v1/consistency?first=${a.sth.size}`);
60
- ok = verifyConsistency(a.sth.size, sth.size, toRoot(a.sth.root), toRoot(sth.root), c.proof.map(hexToBytes));
68
+ let ok = verifySTH(a.sth) && a.sth.log_id === sth.log_id;
69
+ if (ok && a.sth.size > sth.size) {
70
+ // anchored after our snapshot — a valid STH this snapshot cannot relate to yet;
71
+ // its extension is checked next run, once the observed head has caught up
72
+ push(`anchor ${short(a.ref)} newer than snapshot (extension checked next run)`, true);
73
+ continue;
61
74
  }
75
+ if (ok && a.sth.size === sth.size)
76
+ ok = a.sth.root === sth.root; // same size, different root = equivocation
77
+ if (ok && a.sth.size < sth.size)
78
+ ok = await verifyExtension(a.sth.size, a.sth.root);
62
79
  push(`anchor ${short(a.ref)} valid + extended`, ok, `anchor ${short(a.ref)}: invalid STH or current tree is not an append-only extension of it`);
63
80
  }
64
81
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zanii/monitor",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Independent Zanii log monitor: continuously verify the transparency log is append-only (consistency proofs) and that anchored checkpoints hold. Makes 'trust no one' operational.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://ledger.zanii.agency",
@@ -30,13 +30,13 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@noble/hashes": "^1.7.1",
33
- "@zanii/core": "0.2.0"
33
+ "@zanii/core": "^0.5.2"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^26.1.0",
37
37
  "typescript": "^5.8.2",
38
38
  "vitest": "^3.0.9",
39
- "@zanii/testing": "0.1.0"
39
+ "@zanii/testing": "0.1.2"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "tsc -p tsconfig.build.json",
package/src/index.ts CHANGED
@@ -61,13 +61,27 @@ export async function checkOnce(
61
61
  const checks: Check[] = [];
62
62
  const violations: string[] = [];
63
63
  const push = (name: string, ok: boolean, detail?: string) => {
64
- checks.push({ name, ok, detail });
64
+ checks.push(ok ? { name, ok } : { name, ok, detail });
65
65
  if (!ok) violations.push(detail ?? name);
66
66
  };
67
67
 
68
68
  const sth = (await get('/v1/sth')) as SignedTreeHead;
69
69
  push('signed tree head signature', verifySTH(sth) && !!sth.log_id, 'STH signature invalid');
70
70
 
71
+ // Prove `firstRoot@first` is an append-only prefix of the snapshot head. The proof is
72
+ // PINNED to the snapshot size (SPEC: ?first=m&second=n), so writes landing during the
73
+ // run cannot race the comparison into a false "history rewritten" alarm (TOCTOU).
74
+ // A pre-`second` server ignores the pin and proves against its live tree; we then
75
+ // verify against the (size, root) it actually proved — still catches any rewrite of
76
+ // history up to `first`, at the cost of trusting the served head for the newest tail.
77
+ const verifyExtension = async (first: number, firstRoot: string): Promise<boolean> => {
78
+ if (first === 0) return true; // the empty tree is trivially a prefix
79
+ const c = await get(`/v1/consistency?first=${first}&second=${sth.size}`);
80
+ const pinned = c.second === sth.size;
81
+ return verifyConsistency(first, pinned ? sth.size : c.second, toRoot(firstRoot),
82
+ toRoot(pinned ? sth.root : c.second_root), (c.proof as string[]).map(hexToBytes));
83
+ };
84
+
71
85
  if (prev) {
72
86
  if (sth.size < prev.size) {
73
87
  push(`append-only (size ${sth.size} ≥ ${prev.size})`, false,
@@ -76,10 +90,7 @@ export async function checkOnce(
76
90
  push('append-only (root unchanged at same size)', sth.root === prev.root,
77
91
  `root changed at unchanged size ${sth.size} — history was rewritten`);
78
92
  } else {
79
- const c = await get(`/v1/consistency?first=${prev.size}`);
80
- const ok = verifyConsistency(prev.size, sth.size, toRoot(prev.root), toRoot(sth.root),
81
- (c.proof as string[]).map(hexToBytes));
82
- push(`append-only (consistency ${prev.size}→${sth.size})`, ok,
93
+ push(`append-only (consistency ${prev.size}→${sth.size})`, await verifyExtension(prev.size, prev.root),
83
94
  `consistency proof ${prev.size}→${sth.size} FAILED — history was rewritten`);
84
95
  }
85
96
  }
@@ -88,12 +99,15 @@ export async function checkOnce(
88
99
  try {
89
100
  const { anchors } = (await get('/v1/anchors')) as { anchors?: Array<{ sth: SignedTreeHead; ref?: unknown }> };
90
101
  for (const a of anchors ?? []) {
91
- let ok = verifySTH(a.sth) && a.sth.log_id === sth.log_id && a.sth.size <= sth.size;
92
- if (ok && a.sth.size < sth.size) {
93
- const c = await get(`/v1/consistency?first=${a.sth.size}`);
94
- ok = verifyConsistency(a.sth.size, sth.size, toRoot(a.sth.root), toRoot(sth.root),
95
- (c.proof as string[]).map(hexToBytes));
102
+ let ok = verifySTH(a.sth) && a.sth.log_id === sth.log_id;
103
+ if (ok && a.sth.size > sth.size) {
104
+ // anchored after our snapshot — a valid STH this snapshot cannot relate to yet;
105
+ // its extension is checked next run, once the observed head has caught up
106
+ push(`anchor ${short(a.ref)} newer than snapshot (extension checked next run)`, true);
107
+ continue;
96
108
  }
109
+ if (ok && a.sth.size === sth.size) ok = a.sth.root === sth.root; // same size, different root = equivocation
110
+ if (ok && a.sth.size < sth.size) ok = await verifyExtension(a.sth.size, a.sth.root);
97
111
  push(`anchor ${short(a.ref)} valid + extended`, ok,
98
112
  `anchor ${short(a.ref)}: invalid STH or current tree is not an append-only extension of it`);
99
113
  }