@zanii/sentinel 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -134,6 +134,23 @@ export interface Watcher {
134
134
  * `scan` from `receipt.recorded` webhooks (`@zanii/webhooks`) instead of polling.
135
135
  */
136
136
  export declare function createWatcher(opts: WatcherOptions): Watcher;
137
+ export interface BlastRadius {
138
+ /** What the delegation allows — the could-reach surface. */
139
+ declared_scopes: string[];
140
+ /** Target prefixes actually observed in history — the did-reach surface. */
141
+ used_prefixes: string[];
142
+ /** Declared scopes with NO observed activity underneath — standing, unused authority. */
143
+ unused_scopes: string[];
144
+ /** Observed prefixes not covered by any declared scope (should be impossible on-ledger). */
145
+ undeclared_prefixes: string[];
146
+ }
147
+ /**
148
+ * Could-reach vs did-reach. Standing authority an agent never uses is pure risk —
149
+ * the first thing to cut after an incident, and the first thing an underwriter asks
150
+ * about (`insurance` flags WILDCARD_AUTHORITY; this shows the whole surface). Facts,
151
+ * not a score — a `*` scope is reported as one unused scope, not silently expanded.
152
+ */
153
+ export declare function blastRadius(baseline: Baseline): BlastRadius;
137
154
  /**
138
155
  * The default response: a `@zanii/policy` rule turning the flagged prefixes into a
139
156
  * `require_approval` speed bump.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -20,28 +20,44 @@
20
20
  * bump, never auto-revoke; in-scope compromise is the hard case — sequences and rates
21
21
  * narrow the window, never to zero.
22
22
  */
23
- import { jcsHash, scopesAllow, verifyChain } from '@zanii/core';
23
+ import { jcsHash, receiptHash, scopesAllow, verifyChain } from '@zanii/core';
24
24
  export function targetPrefix(target) {
25
25
  return target.split('.').slice(0, 2).join('.');
26
26
  }
27
- function hourOf(ts) {
28
- return new Date(ts).getUTCHours();
27
+ /** Epoch ms; a naive ts (no offset) is treated as UTC — never local. `null` = unparseable. */
28
+ function tsMs(ts) {
29
+ if (typeof ts !== 'string' || !/^\d{4}-\d{2}-\d{2}T/.test(ts))
30
+ return null;
31
+ const ms = Date.parse(/[zZ]$|[+-]\d{2}:?\d{2}$/.test(ts) ? ts : `${ts}Z`);
32
+ return Number.isNaN(ms) ? null : ms;
29
33
  }
34
+ const hourOfMs = (ms) => new Date(ms).getUTCHours();
35
+ /** Hour bucket as `YYYY-MM-DDTHH` in UTC — offset timestamps land in the right bucket. */
36
+ const hourBucket = (ms) => new Date(Math.floor(ms / 3_600_000) * 3_600_000).toISOString().slice(0, 13);
30
37
  /** Build the drift baseline for one agent from its delegation (declared) + history (learned). */
31
38
  export function buildBaseline(agentId, opts = {}) {
32
39
  const history = (opts.receipts ?? []).filter((r) => r.agent_id === agentId);
33
- const scopes = opts.delegation
34
- ? verifyChain(opts.delegation, { agentId }).effectiveScopes ?? []
35
- : [];
40
+ let scopes = [];
41
+ if (opts.delegation) {
42
+ const chain = verifyChain(opts.delegation, { agentId });
43
+ // A failed chain must NOT silently degrade to scopes:[] — scan treats empty scopes as
44
+ // "everything in scope", so detection severity would quietly drop.
45
+ if (!chain.ok)
46
+ throw new Error(`delegation chain failed verification (${chain.error}) — refusing to build a baseline on it`);
47
+ scopes = chain.effectiveScopes ?? [];
48
+ }
36
49
  const prefixes = new Set();
37
50
  const hours = new Array(24).fill(0);
38
51
  const perHourCounts = new Map();
39
52
  let intents = 0;
40
53
  let actions = 0;
41
54
  for (const r of history) {
55
+ const ms = tsMs(r.ts);
56
+ if (ms === null)
57
+ throw new Error(`receipt has an unparseable ts: ${String(r.ts)}`);
42
58
  prefixes.add(targetPrefix(r.target));
43
- hours[hourOf(r.ts)] = (hours[hourOf(r.ts)] ?? 0) + 1;
44
- const bucket = r.ts.slice(0, 13); // YYYY-MM-DDTHH
59
+ hours[hourOfMs(ms)] = (hours[hourOfMs(ms)] ?? 0) + 1;
60
+ const bucket = hourBucket(ms);
45
61
  perHourCounts.set(bucket, (perHourCounts.get(bucket) ?? 0) + 1);
46
62
  if (r.action === 'action_intent')
47
63
  intents += 1;
@@ -81,17 +97,39 @@ function finding(rule, severity, agentId, evidence, observed, expected) {
81
97
  targets: [...new Set(evidence.map((r) => r.target))],
82
98
  observed,
83
99
  expected,
84
- ts: evidence[evidence.length - 1]?.ts ?? new Date(0).toISOString(),
100
+ ts: evidence[evidence.length - 1]?.ts ?? '1970-01-01T00:00:00.000Z',
85
101
  };
86
102
  }
87
103
  /** Scan a window of an agent's receipts against its baseline. Pure — no I/O. */
88
104
  export function scan(receipts, baseline, opts = {}) {
89
105
  const findings = [];
90
- const mine = receipts
91
- .filter((r) => r.agent_id === baseline.agent_id)
92
- .sort((a, b) => a.ts.localeCompare(b.ts));
106
+ const mine = [];
107
+ const malformed = [];
108
+ for (const r of receipts) {
109
+ if (r.agent_id !== baseline.agent_id)
110
+ continue;
111
+ (tsMs(r.ts) === null ? malformed : mine).push(r);
112
+ }
113
+ mine.sort((a, b) => tsMs(a.ts) - tsMs(b.ts));
114
+ // A receipt we cannot place in time is excluded from every windowed detector — that
115
+ // exclusion must never be silent.
116
+ if (malformed.length > 0) {
117
+ findings.push(finding('malformed-timestamp', 'medium', baseline.agent_id, malformed, `${malformed.length} receipt(s) with unparseable ts — excluded from windowed detectors`, 'parseable ISO timestamps'));
118
+ }
93
119
  const minSample = opts.minSample ?? 20;
94
120
  const known = new Set(baseline.target_prefixes);
121
+ // 0. chain-gap — receipts carry `prev`; a broken link inside the window means a receipt
122
+ // between the two is missing or was suppressed. A gap is NEVER clean.
123
+ for (let i = 1; i < mine.length; i++) {
124
+ const before = mine[i - 1];
125
+ const cur = mine[i];
126
+ if (typeof cur.prev !== 'string')
127
+ continue; // chain start / foreign data — unverifiable, not a gap
128
+ const { hash: _h, ...bare } = before;
129
+ if (cur.prev !== receiptHash(bare)) {
130
+ findings.push(finding('chain-gap', 'high', baseline.agent_id, [before, cur], `receipt chain broken between ${before.ts} and ${cur.ts} — a receipt in between is missing or was suppressed`, 'contiguous prev linkage — a gap is UNKNOWN, not clean'));
131
+ }
132
+ }
95
133
  // 1. novel-target — prefix outside the learned set; high severity if also outside declared scopes
96
134
  const novel = new Map();
97
135
  for (const r of mine) {
@@ -111,14 +149,16 @@ export function scan(receipts, baseline, opts = {}) {
111
149
  rule: 'scope-edge', severity: probes.length > 2 ? 'high' : 'medium', agent_id: baseline.agent_id,
112
150
  receipt_hashes: [], targets: [],
113
151
  observed: `${probes.length} rejected write(s): ${probes.map((p) => p.error).filter(Boolean).slice(0, 3).join('; ')}`,
114
- expected: 'no rejected writes', ts: probes[probes.length - 1]?.ts ?? new Date(0).toISOString(),
152
+ expected: 'no rejected writes', ts: probes[probes.length - 1]?.ts ?? '1970-01-01T00:00:00.000Z',
115
153
  });
116
154
  }
117
155
  // 3. rate-spike — actions per hour vs baseline (milli-units; +1000 keeps std=0 from firing on +1)
118
156
  if (baseline.sample_size >= minSample) {
119
157
  const buckets = new Map();
120
- for (const r of mine)
121
- buckets.set(r.ts.slice(0, 13), [...(buckets.get(r.ts.slice(0, 13)) ?? []), r]);
158
+ for (const r of mine) {
159
+ const b = hourBucket(tsMs(r.ts));
160
+ buckets.set(b, [...(buckets.get(b) ?? []), r]);
161
+ }
122
162
  const z = opts.rateZ ?? 3;
123
163
  for (const [hour, rs] of buckets) {
124
164
  if (rs.length * 1000 > baseline.rate.mean_milli + z * (baseline.rate.std_milli + 1000)) {
@@ -139,32 +179,39 @@ export function scan(receipts, baseline, opts = {}) {
139
179
  for (const rule of opts.sequences ?? [EXFIL_SEQUENCE]) {
140
180
  let step = 0;
141
181
  let startMs = 0;
142
- const evidence = [];
182
+ let evidence = [];
143
183
  for (const r of mine) {
184
+ const ms = tsMs(r.ts);
185
+ // stale window: reset FIRST, then re-test this receipt — it may open a new sequence
186
+ if (step > 0 && ms - startMs > rule.windowMs) {
187
+ step = 0;
188
+ evidence = [];
189
+ }
144
190
  if (rule.steps[step].test(r.target)) {
145
191
  if (step === 0)
146
- startMs = Date.parse(r.ts);
147
- if (Date.parse(r.ts) - startMs <= rule.windowMs) {
148
- evidence.push(r);
149
- step += 1;
150
- if (step === rule.steps.length) {
151
- findings.push(finding(`sequence:${rule.name}`, 'high', baseline.agent_id, [...evidence], evidence.map((e) => e.target).join(' → '), `no ${rule.name} sequence`));
152
- step = 0;
153
- evidence.length = 0;
154
- }
155
- }
156
- else {
192
+ startMs = ms;
193
+ evidence.push(r);
194
+ step += 1;
195
+ if (step === rule.steps.length) {
196
+ findings.push(finding(`sequence:${rule.name}`, 'high', baseline.agent_id, [...evidence], evidence.map((e) => e.target).join(' → '), `no ${rule.name} sequence`));
157
197
  step = 0;
158
- evidence.length = 0;
198
+ evidence = [];
159
199
  }
160
200
  }
201
+ else if (step > 0 && rule.steps[0].test(r.target)) {
202
+ // a fresh step-0 match mid-sequence restarts the window from HERE — otherwise an
203
+ // early step-0 pins a stale start and a later, real sequence is never seen
204
+ startMs = ms;
205
+ evidence = [r];
206
+ step = 1;
207
+ }
161
208
  }
162
209
  }
163
210
  // 6. off-hours — activity in hours the baseline says are silent
164
211
  if (baseline.sample_size >= minSample) {
165
- const silent = mine.filter((r) => (baseline.active_hours[hourOf(r.ts)] ?? 0) === 0);
212
+ const silent = mine.filter((r) => (baseline.active_hours[hourOfMs(tsMs(r.ts))] ?? 0) === 0);
166
213
  if (silent.length > 0) {
167
- findings.push(finding('off-hours', 'medium', baseline.agent_id, silent, `${silent.length} action(s) at UTC hour(s) [${[...new Set(silent.map((r) => hourOf(r.ts)))].join(',')}]`, 'activity only in learned active hours'));
214
+ findings.push(finding('off-hours', 'medium', baseline.agent_id, silent, `${silent.length} action(s) at UTC hour(s) [${[...new Set(silent.map((r) => hourOfMs(tsMs(r.ts))))].join(',')}]`, 'activity only in learned active hours'));
168
215
  }
169
216
  }
170
217
  // 7. wall-crossing — operator mode only (raw payloads pre-hash)
@@ -210,17 +257,44 @@ export function createWatcher(opts) {
210
257
  throw new Error(`recent fetch failed: HTTP ${res.status}`);
211
258
  const body = (await res.json());
212
259
  const fresh = body.receipts.filter((r) => r.index > cursor);
260
+ const all = [];
261
+ // "We were not looking" must NEVER render as "nothing happened": /v1/recent is a
262
+ // capped global feed, so if the oldest fresh index doesn't connect to the cursor,
263
+ // receipts were missed between polls. Report the blind spot as a finding — a
264
+ // confident silence is the most dangerous output a monitor can produce.
265
+ if (cursor >= 0 && fresh.length > 0) {
266
+ const oldest = Math.min(...fresh.map((r) => r.index));
267
+ if (oldest > cursor + 1) {
268
+ const gap = {
269
+ rule: 'unknown-window',
270
+ severity: 'medium',
271
+ agent_id: '*',
272
+ receipt_hashes: [],
273
+ targets: [],
274
+ observed: `ledger indices ${cursor + 1}..${oldest - 1} were never scanned (feed outpaced the poll)`,
275
+ expected: 'contiguous coverage — treat the gap as UNKNOWN, not clean; backfill via /v1/recent?offset=',
276
+ ts: fresh.find((r) => r.index === oldest).receipt.ts,
277
+ };
278
+ const fp = fingerprint(gap);
279
+ if (!seen.has(fp)) {
280
+ seen.add(fp);
281
+ all.push(gap);
282
+ await opts.onFinding(gap);
283
+ }
284
+ }
285
+ }
213
286
  for (const r of fresh)
214
287
  cursor = Math.max(cursor, r.index);
215
- const all = [];
216
288
  for (const [did, baseline] of Object.entries(opts.baselines)) {
217
289
  const mine = fresh.filter((r) => r.receipt.agent_id === did).map((r) => ({ ...r.receipt, hash: r.hash }));
218
290
  if (mine.length === 0)
219
291
  continue;
220
292
  // Rolling window: append, then trim to `windowMs` behind the newest receipt.
221
- const buf = [...(buffers[did] ?? []), ...mine].sort((a, b) => a.ts.localeCompare(b.ts));
222
- const newest = Date.parse(buf[buf.length - 1].ts);
223
- buffers[did] = buf.filter((r) => newest - Date.parse(r.ts) <= windowMs);
293
+ // Parsed-UTC ordering (offset timestamps sort correctly); unparseable ts are kept
294
+ // so scan() can report them rather than silently dropping them here.
295
+ const buf = [...(buffers[did] ?? []), ...mine].sort((a, b) => (tsMs(a.ts) ?? 0) - (tsMs(b.ts) ?? 0));
296
+ const newest = tsMs(buf[buf.length - 1].ts) ?? 0;
297
+ buffers[did] = buf.filter((r) => newest - (tsMs(r.ts) ?? newest) <= windowMs);
224
298
  for (const f of scan(buffers[did], baseline, opts.scan)) {
225
299
  const fp = fingerprint(f);
226
300
  if (seen.has(fp))
@@ -236,6 +310,28 @@ export function createWatcher(opts) {
236
310
  },
237
311
  };
238
312
  }
313
+ /**
314
+ * Could-reach vs did-reach. Standing authority an agent never uses is pure risk —
315
+ * the first thing to cut after an incident, and the first thing an underwriter asks
316
+ * about (`insurance` flags WILDCARD_AUTHORITY; this shows the whole surface). Facts,
317
+ * not a score — a `*` scope is reported as one unused scope, not silently expanded.
318
+ */
319
+ export function blastRadius(baseline) {
320
+ // used_prefixes are TRUNCATED to two segments, so a concrete deeper scope like
321
+ // `crm.contacts.read` must relate to the prefix `crm.contacts` in EITHER direction —
322
+ // otherwise a fully compliant agent reads as both unused-authority and undeclared.
323
+ const related = (scope, p) => {
324
+ if (scope === '*')
325
+ return true;
326
+ const s = scope.endsWith('.*') ? scope.slice(0, -2) : scope;
327
+ return s === p || s.startsWith(`${p}.`) || p.startsWith(`${s}.`);
328
+ };
329
+ const declared = [...baseline.scopes].sort();
330
+ const used = [...baseline.target_prefixes].sort();
331
+ const unused = declared.filter((s) => !used.some((p) => related(s, p)));
332
+ const undeclared = used.filter((p) => !declared.some((s) => related(s, p)));
333
+ return { declared_scopes: declared, used_prefixes: used, unused_scopes: unused, undeclared_prefixes: undeclared };
334
+ }
239
335
  /**
240
336
  * The default response: a `@zanii/policy` rule turning the flagged prefixes into a
241
337
  * `require_approval` speed bump.
@@ -245,7 +341,7 @@ export function createWatcher(opts) {
245
341
  * gates the agent's entire surface. That call belongs to a human.
246
342
  */
247
343
  export function escalationRule(f) {
248
- const prefixes = [...new Set(f.targets.map((t) => `${targetPrefix(t)}.*`))];
344
+ const prefixes = [...new Set(f.targets.map((t) => `${targetPrefix(t)}.*`))].sort();
249
345
  if (prefixes.length === 0)
250
346
  return null;
251
347
  return {
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/sentinel",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Runtime behavioral monitoring for agents ('antivirus for agents'): score drift between what an agent actually does and what it was made to do, from the receipt stream — and the alerts themselves are tamper-evident receipts.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://ledger.zanii.agency",
package/src/index.ts CHANGED
@@ -20,7 +20,7 @@
20
20
  * bump, never auto-revoke; in-scope compromise is the hard case — sequences and rates
21
21
  * narrow the window, never to zero.
22
22
  */
23
- import { jcsHash, scopesAllow, verifyChain, type DelegationCert, type Receipt } from '@zanii/core';
23
+ import { jcsHash, receiptHash, scopesAllow, verifyChain, type DelegationCert, type Receipt } from '@zanii/core';
24
24
 
25
25
  export type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
26
26
 
@@ -52,28 +52,42 @@ export function targetPrefix(target: string): string {
52
52
  return target.split('.').slice(0, 2).join('.');
53
53
  }
54
54
 
55
- function hourOf(ts: string): number {
56
- return new Date(ts).getUTCHours();
55
+ /** Epoch ms; a naive ts (no offset) is treated as UTC — never local. `null` = unparseable. */
56
+ function tsMs(ts: unknown): number | null {
57
+ if (typeof ts !== 'string' || !/^\d{4}-\d{2}-\d{2}T/.test(ts)) return null;
58
+ const ms = Date.parse(/[zZ]$|[+-]\d{2}:?\d{2}$/.test(ts) ? ts : `${ts}Z`);
59
+ return Number.isNaN(ms) ? null : ms;
57
60
  }
58
61
 
62
+ const hourOfMs = (ms: number): number => new Date(ms).getUTCHours();
63
+ /** Hour bucket as `YYYY-MM-DDTHH` in UTC — offset timestamps land in the right bucket. */
64
+ const hourBucket = (ms: number): string => new Date(Math.floor(ms / 3_600_000) * 3_600_000).toISOString().slice(0, 13);
65
+
59
66
  /** Build the drift baseline for one agent from its delegation (declared) + history (learned). */
60
67
  export function buildBaseline(
61
68
  agentId: string,
62
69
  opts: { receipts?: Receipt[]; delegation?: DelegationCert[] } = {},
63
70
  ): Baseline {
64
71
  const history = (opts.receipts ?? []).filter((r) => r.agent_id === agentId);
65
- const scopes = opts.delegation
66
- ? verifyChain(opts.delegation, { agentId }).effectiveScopes ?? []
67
- : [];
72
+ let scopes: string[] = [];
73
+ if (opts.delegation) {
74
+ const chain = verifyChain(opts.delegation, { agentId });
75
+ // A failed chain must NOT silently degrade to scopes:[] — scan treats empty scopes as
76
+ // "everything in scope", so detection severity would quietly drop.
77
+ if (!chain.ok) throw new Error(`delegation chain failed verification (${chain.error}) — refusing to build a baseline on it`);
78
+ scopes = chain.effectiveScopes ?? [];
79
+ }
68
80
  const prefixes = new Set<string>();
69
81
  const hours = new Array<number>(24).fill(0);
70
82
  const perHourCounts = new Map<string, number>();
71
83
  let intents = 0;
72
84
  let actions = 0;
73
85
  for (const r of history) {
86
+ const ms = tsMs(r.ts);
87
+ if (ms === null) throw new Error(`receipt has an unparseable ts: ${String(r.ts)}`);
74
88
  prefixes.add(targetPrefix(r.target));
75
- hours[hourOf(r.ts)] = (hours[hourOf(r.ts)] ?? 0) + 1;
76
- const bucket = r.ts.slice(0, 13); // YYYY-MM-DDTHH
89
+ hours[hourOfMs(ms)] = (hours[hourOfMs(ms)] ?? 0) + 1;
90
+ const bucket = hourBucket(ms);
77
91
  perHourCounts.set(bucket, (perHourCounts.get(bucket) ?? 0) + 1);
78
92
  if (r.action === 'action_intent') intents += 1;
79
93
  else actions += 1;
@@ -157,19 +171,44 @@ function finding(rule: string, severity: Severity, agentId: string, evidence: Sc
157
171
  targets: [...new Set(evidence.map((r) => r.target))],
158
172
  observed,
159
173
  expected,
160
- ts: evidence[evidence.length - 1]?.ts ?? new Date(0).toISOString(),
174
+ ts: evidence[evidence.length - 1]?.ts ?? '1970-01-01T00:00:00.000Z',
161
175
  };
162
176
  }
163
177
 
164
178
  /** Scan a window of an agent's receipts against its baseline. Pure — no I/O. */
165
179
  export function scan(receipts: Scanned[], baseline: Baseline, opts: ScanOptions = {}): Finding[] {
166
180
  const findings: Finding[] = [];
167
- const mine = receipts
168
- .filter((r) => r.agent_id === baseline.agent_id)
169
- .sort((a, b) => a.ts.localeCompare(b.ts));
181
+ const mine: Scanned[] = [];
182
+ const malformed: Scanned[] = [];
183
+ for (const r of receipts) {
184
+ if (r.agent_id !== baseline.agent_id) continue;
185
+ (tsMs(r.ts) === null ? malformed : mine).push(r);
186
+ }
187
+ mine.sort((a, b) => tsMs(a.ts)! - tsMs(b.ts)!);
188
+ // A receipt we cannot place in time is excluded from every windowed detector — that
189
+ // exclusion must never be silent.
190
+ if (malformed.length > 0) {
191
+ findings.push(finding('malformed-timestamp', 'medium', baseline.agent_id, malformed,
192
+ `${malformed.length} receipt(s) with unparseable ts — excluded from windowed detectors`,
193
+ 'parseable ISO timestamps'));
194
+ }
170
195
  const minSample = opts.minSample ?? 20;
171
196
  const known = new Set(baseline.target_prefixes);
172
197
 
198
+ // 0. chain-gap — receipts carry `prev`; a broken link inside the window means a receipt
199
+ // between the two is missing or was suppressed. A gap is NEVER clean.
200
+ for (let i = 1; i < mine.length; i++) {
201
+ const before = mine[i - 1]!;
202
+ const cur = mine[i]!;
203
+ if (typeof cur.prev !== 'string') continue; // chain start / foreign data — unverifiable, not a gap
204
+ const { hash: _h, ...bare } = before;
205
+ if (cur.prev !== receiptHash(bare as Receipt)) {
206
+ findings.push(finding('chain-gap', 'high', baseline.agent_id, [before, cur],
207
+ `receipt chain broken between ${before.ts} and ${cur.ts} — a receipt in between is missing or was suppressed`,
208
+ 'contiguous prev linkage — a gap is UNKNOWN, not clean'));
209
+ }
210
+ }
211
+
173
212
  // 1. novel-target — prefix outside the learned set; high severity if also outside declared scopes
174
213
  const novel = new Map<string, Scanned[]>();
175
214
  for (const r of mine) {
@@ -191,14 +230,17 @@ export function scan(receipts: Scanned[], baseline: Baseline, opts: ScanOptions
191
230
  rule: 'scope-edge', severity: probes.length > 2 ? 'high' : 'medium', agent_id: baseline.agent_id,
192
231
  receipt_hashes: [], targets: [],
193
232
  observed: `${probes.length} rejected write(s): ${probes.map((p) => p.error).filter(Boolean).slice(0, 3).join('; ')}`,
194
- expected: 'no rejected writes', ts: probes[probes.length - 1]?.ts ?? new Date(0).toISOString(),
233
+ expected: 'no rejected writes', ts: probes[probes.length - 1]?.ts ?? '1970-01-01T00:00:00.000Z',
195
234
  });
196
235
  }
197
236
 
198
237
  // 3. rate-spike — actions per hour vs baseline (milli-units; +1000 keeps std=0 from firing on +1)
199
238
  if (baseline.sample_size >= minSample) {
200
239
  const buckets = new Map<string, Scanned[]>();
201
- for (const r of mine) buckets.set(r.ts.slice(0, 13), [...(buckets.get(r.ts.slice(0, 13)) ?? []), r]);
240
+ for (const r of mine) {
241
+ const b = hourBucket(tsMs(r.ts)!);
242
+ buckets.set(b, [...(buckets.get(b) ?? []), r]);
243
+ }
202
244
  const z = opts.rateZ ?? 3;
203
245
  for (const [hour, rs] of buckets) {
204
246
  if (rs.length * 1000 > baseline.rate.mean_milli + z * (baseline.rate.std_milli + 1000)) {
@@ -225,33 +267,40 @@ export function scan(receipts: Scanned[], baseline: Baseline, opts: ScanOptions
225
267
  for (const rule of opts.sequences ?? [EXFIL_SEQUENCE]) {
226
268
  let step = 0;
227
269
  let startMs = 0;
228
- const evidence: Scanned[] = [];
270
+ let evidence: Scanned[] = [];
229
271
  for (const r of mine) {
272
+ const ms = tsMs(r.ts)!;
273
+ // stale window: reset FIRST, then re-test this receipt — it may open a new sequence
274
+ if (step > 0 && ms - startMs > rule.windowMs) {
275
+ step = 0;
276
+ evidence = [];
277
+ }
230
278
  if (rule.steps[step]!.test(r.target)) {
231
- if (step === 0) startMs = Date.parse(r.ts);
232
- if (Date.parse(r.ts) - startMs <= rule.windowMs) {
233
- evidence.push(r);
234
- step += 1;
235
- if (step === rule.steps.length) {
236
- findings.push(finding(`sequence:${rule.name}`, 'high', baseline.agent_id, [...evidence],
237
- evidence.map((e) => e.target).join(' → '), `no ${rule.name} sequence`));
238
- step = 0;
239
- evidence.length = 0;
240
- }
241
- } else {
279
+ if (step === 0) startMs = ms;
280
+ evidence.push(r);
281
+ step += 1;
282
+ if (step === rule.steps.length) {
283
+ findings.push(finding(`sequence:${rule.name}`, 'high', baseline.agent_id, [...evidence],
284
+ evidence.map((e) => e.target).join(' → '), `no ${rule.name} sequence`));
242
285
  step = 0;
243
- evidence.length = 0;
286
+ evidence = [];
244
287
  }
288
+ } else if (step > 0 && rule.steps[0]!.test(r.target)) {
289
+ // a fresh step-0 match mid-sequence restarts the window from HERE — otherwise an
290
+ // early step-0 pins a stale start and a later, real sequence is never seen
291
+ startMs = ms;
292
+ evidence = [r];
293
+ step = 1;
245
294
  }
246
295
  }
247
296
  }
248
297
 
249
298
  // 6. off-hours — activity in hours the baseline says are silent
250
299
  if (baseline.sample_size >= minSample) {
251
- const silent = mine.filter((r) => (baseline.active_hours[hourOf(r.ts)] ?? 0) === 0);
300
+ const silent = mine.filter((r) => (baseline.active_hours[hourOfMs(tsMs(r.ts)!)] ?? 0) === 0);
252
301
  if (silent.length > 0) {
253
302
  findings.push(finding('off-hours', 'medium', baseline.agent_id, silent,
254
- `${silent.length} action(s) at UTC hour(s) [${[...new Set(silent.map((r) => hourOf(r.ts)))].join(',')}]`,
303
+ `${silent.length} action(s) at UTC hour(s) [${[...new Set(silent.map((r) => hourOfMs(tsMs(r.ts)!)))].join(',')}]`,
255
304
  'activity only in learned active hours'));
256
305
  }
257
306
  }
@@ -333,17 +382,44 @@ export function createWatcher(opts: WatcherOptions): Watcher {
333
382
  if (!res.ok) throw new Error(`recent fetch failed: HTTP ${res.status}`);
334
383
  const body = (await res.json()) as { receipts: Array<{ receipt: Receipt; hash: string; index: number }> };
335
384
  const fresh = body.receipts.filter((r) => r.index > cursor);
336
- for (const r of fresh) cursor = Math.max(cursor, r.index);
337
385
 
338
386
  const all: Finding[] = [];
387
+ // "We were not looking" must NEVER render as "nothing happened": /v1/recent is a
388
+ // capped global feed, so if the oldest fresh index doesn't connect to the cursor,
389
+ // receipts were missed between polls. Report the blind spot as a finding — a
390
+ // confident silence is the most dangerous output a monitor can produce.
391
+ if (cursor >= 0 && fresh.length > 0) {
392
+ const oldest = Math.min(...fresh.map((r) => r.index));
393
+ if (oldest > cursor + 1) {
394
+ const gap: Finding = {
395
+ rule: 'unknown-window',
396
+ severity: 'medium',
397
+ agent_id: '*',
398
+ receipt_hashes: [],
399
+ targets: [],
400
+ observed: `ledger indices ${cursor + 1}..${oldest - 1} were never scanned (feed outpaced the poll)`,
401
+ expected: 'contiguous coverage — treat the gap as UNKNOWN, not clean; backfill via /v1/recent?offset=',
402
+ ts: fresh.find((r) => r.index === oldest)!.receipt.ts,
403
+ };
404
+ const fp = fingerprint(gap);
405
+ if (!seen.has(fp)) {
406
+ seen.add(fp);
407
+ all.push(gap);
408
+ await opts.onFinding(gap);
409
+ }
410
+ }
411
+ }
412
+ for (const r of fresh) cursor = Math.max(cursor, r.index);
339
413
  for (const [did, baseline] of Object.entries(opts.baselines)) {
340
414
  const mine = fresh.filter((r) => r.receipt.agent_id === did).map((r) => ({ ...r.receipt, hash: r.hash }));
341
415
  if (mine.length === 0) continue;
342
416
 
343
417
  // Rolling window: append, then trim to `windowMs` behind the newest receipt.
344
- const buf = [...(buffers[did] ?? []), ...mine].sort((a, b) => a.ts.localeCompare(b.ts));
345
- const newest = Date.parse(buf[buf.length - 1]!.ts);
346
- buffers[did] = buf.filter((r) => newest - Date.parse(r.ts) <= windowMs);
418
+ // Parsed-UTC ordering (offset timestamps sort correctly); unparseable ts are kept
419
+ // so scan() can report them rather than silently dropping them here.
420
+ const buf = [...(buffers[did] ?? []), ...mine].sort((a, b) => (tsMs(a.ts) ?? 0) - (tsMs(b.ts) ?? 0));
421
+ const newest = tsMs(buf[buf.length - 1]!.ts) ?? 0;
422
+ buffers[did] = buf.filter((r) => newest - (tsMs(r.ts) ?? newest) <= windowMs);
347
423
 
348
424
  for (const f of scan(buffers[did]!, baseline, opts.scan)) {
349
425
  const fp = fingerprint(f);
@@ -359,6 +435,41 @@ export function createWatcher(opts: WatcherOptions): Watcher {
359
435
  };
360
436
  }
361
437
 
438
+ export interface BlastRadius {
439
+ /** What the delegation allows — the could-reach surface. */
440
+ declared_scopes: string[];
441
+ /** Target prefixes actually observed in history — the did-reach surface. */
442
+ used_prefixes: string[];
443
+ /** Declared scopes with NO observed activity underneath — standing, unused authority. */
444
+ unused_scopes: string[];
445
+ /** Observed prefixes not covered by any declared scope (should be impossible on-ledger). */
446
+ undeclared_prefixes: string[];
447
+ }
448
+
449
+ /**
450
+ * Could-reach vs did-reach. Standing authority an agent never uses is pure risk —
451
+ * the first thing to cut after an incident, and the first thing an underwriter asks
452
+ * about (`insurance` flags WILDCARD_AUTHORITY; this shows the whole surface). Facts,
453
+ * not a score — a `*` scope is reported as one unused scope, not silently expanded.
454
+ */
455
+ export function blastRadius(baseline: Baseline): BlastRadius {
456
+ // used_prefixes are TRUNCATED to two segments, so a concrete deeper scope like
457
+ // `crm.contacts.read` must relate to the prefix `crm.contacts` in EITHER direction —
458
+ // otherwise a fully compliant agent reads as both unused-authority and undeclared.
459
+ const related = (scope: string, p: string): boolean => {
460
+ if (scope === '*') return true;
461
+ const s = scope.endsWith('.*') ? scope.slice(0, -2) : scope;
462
+ return s === p || s.startsWith(`${p}.`) || p.startsWith(`${s}.`);
463
+ };
464
+ const declared = [...baseline.scopes].sort();
465
+ const used = [...baseline.target_prefixes].sort();
466
+ const unused = declared.filter((s) => !used.some((p) => related(s, p)));
467
+ const undeclared = used.filter(
468
+ (p) => !declared.some((s) => related(s, p)),
469
+ );
470
+ return { declared_scopes: declared, used_prefixes: used, unused_scopes: unused, undeclared_prefixes: undeclared };
471
+ }
472
+
362
473
  /**
363
474
  * The default response: a `@zanii/policy` rule turning the flagged prefixes into a
364
475
  * `require_approval` speed bump.
@@ -368,7 +479,7 @@ export function createWatcher(opts: WatcherOptions): Watcher {
368
479
  * gates the agent's entire surface. That call belongs to a human.
369
480
  */
370
481
  export function escalationRule(f: Finding): { id: string; effect: 'require_approval'; targets: string[] } | null {
371
- const prefixes = [...new Set(f.targets.map((t) => `${targetPrefix(t)}.*`))];
482
+ const prefixes = [...new Set(f.targets.map((t) => `${targetPrefix(t)}.*`))].sort();
372
483
  if (prefixes.length === 0) return null;
373
484
  return {
374
485
  id: `sentinel-${f.rule.replace(/[^a-z0-9-]/gi, '-')}-${f.agent_id.slice(-8)}`,