instar 1.3.366 → 1.3.368
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/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +282 -2
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +29 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/CommitmentsSync.d.ts +144 -0
- package/dist/core/CommitmentsSync.d.ts.map +1 -0
- package/dist/core/CommitmentsSync.js +293 -0
- package/dist/core/CommitmentsSync.js.map +1 -0
- package/dist/core/MeshRpc.d.ts +14 -0
- package/dist/core/MeshRpc.d.ts.map +1 -1
- package/dist/core/MeshRpc.js +7 -1
- package/dist/core/MeshRpc.js.map +1 -1
- package/dist/core/PeerPresencePuller.d.ts +25 -0
- package/dist/core/PeerPresencePuller.d.ts.map +1 -1
- package/dist/core/PeerPresencePuller.js +12 -0
- package/dist/core/PeerPresencePuller.js.map +1 -1
- package/dist/core/PeerVisibilityGuard.d.ts +83 -0
- package/dist/core/PeerVisibilityGuard.d.ts.map +1 -0
- package/dist/core/PeerVisibilityGuard.js +204 -0
- package/dist/core/PeerVisibilityGuard.js.map +1 -0
- package/dist/core/PendingPullLedger.d.ts +129 -0
- package/dist/core/PendingPullLedger.d.ts.map +1 -0
- package/dist/core/PendingPullLedger.js +239 -0
- package/dist/core/PendingPullLedger.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +20 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/WorkingSetManifest.d.ts +2 -0
- package/dist/core/WorkingSetManifest.d.ts.map +1 -1
- package/dist/core/WorkingSetManifest.js +13 -1
- package/dist/core/WorkingSetManifest.js.map +1 -1
- package/dist/core/WorkingSetPull.d.ts +157 -0
- package/dist/core/WorkingSetPull.d.ts.map +1 -0
- package/dist/core/WorkingSetPull.js +546 -0
- package/dist/core/WorkingSetPull.js.map +1 -0
- package/dist/core/WorkingSetPullCoordinator.d.ts +134 -0
- package/dist/core/WorkingSetPullCoordinator.d.ts.map +1 -0
- package/dist/core/WorkingSetPullCoordinator.js +306 -0
- package/dist/core/WorkingSetPullCoordinator.js.map +1 -0
- package/dist/core/types.d.ts +39 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/CommitmentTracker.d.ts +40 -0
- package/dist/monitoring/CommitmentTracker.d.ts.map +1 -1
- package/dist/monitoring/CommitmentTracker.js +95 -4
- package/dist/monitoring/CommitmentTracker.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +4 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +6 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +2 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +9 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +44 -1
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +64 -64
- package/src/data/state-coherence-registry.json +61 -13
- package/src/scaffold/templates.ts +4 -0
- package/upgrades/1.3.367.md +134 -0
- package/upgrades/1.3.368.md +52 -0
- package/upgrades/side-effects/commitments-sync-p15a.md +93 -0
- package/upgrades/side-effects/working-set-ci-absorb.md +29 -0
- package/upgrades/side-effects/working-set-parity-absorb.md +35 -0
- package/upgrades/side-effects/working-set-pull-p22a.md +121 -0
- package/upgrades/side-effects/working-set-trigger-p22b.md +115 -0
package/dist/core/MeshRpc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MeshRpc.js","sourceRoot":"","sources":["../../src/core/MeshRpc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"MeshRpc.js","sourceRoot":"","sources":["../../src/core/MeshRpc.ts"],"names":[],"mappings":"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"}
|
|
@@ -44,6 +44,15 @@ export interface PeerCapacity {
|
|
|
44
44
|
incarnation: string;
|
|
45
45
|
lastSeq: number;
|
|
46
46
|
}>;
|
|
47
|
+
/**
|
|
48
|
+
* The peer's OWN commitments-store advert (COMMITMENTS-COHERENCE-SPEC
|
|
49
|
+
* §3.2). Present only when the peer runs the commitments-sync layer; old
|
|
50
|
+
* peers omit it and the drive is a no-op.
|
|
51
|
+
*/
|
|
52
|
+
commitmentsAdvert?: {
|
|
53
|
+
incarnation: string;
|
|
54
|
+
replicationSeq: number;
|
|
55
|
+
};
|
|
47
56
|
}
|
|
48
57
|
/** One stream slice of a journal-sync delta (mirrors MeshRpc's `journal-sync.batch`). */
|
|
49
58
|
export interface JournalDeltaStream {
|
|
@@ -90,6 +99,22 @@ export interface PeerPresencePullerDeps {
|
|
|
90
99
|
incarnation: string;
|
|
91
100
|
lastSeq: number;
|
|
92
101
|
}>;
|
|
102
|
+
/**
|
|
103
|
+
* Fired once per peer recorded online this pass (WORKING-SET-HANDOFF-SPEC
|
|
104
|
+
* §3.4 — the pending-pull re-arm rides the same cadence journal-sync does).
|
|
105
|
+
* MUST NOT throw into the puller; consumers dedupe/stagger themselves.
|
|
106
|
+
*/
|
|
107
|
+
onPeerRecorded?: (machineId: string) => void;
|
|
108
|
+
/**
|
|
109
|
+
* REPLICATION-GATED commitments-sync drive (COMMITMENTS-COHERENCE-SPEC
|
|
110
|
+
* §3.2): called with the peer's commitments advert when present; the
|
|
111
|
+
* consumer compares against its replica cursor and pulls delta pages
|
|
112
|
+
* (bounded per tick). MUST NOT throw into the puller.
|
|
113
|
+
*/
|
|
114
|
+
driveCommitmentsSync?: (machineId: string, url: string, advert: {
|
|
115
|
+
incarnation: string;
|
|
116
|
+
replicationSeq: number;
|
|
117
|
+
}) => Promise<void>;
|
|
93
118
|
}
|
|
94
119
|
export declare class PeerPresencePuller {
|
|
95
120
|
private readonly d;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PeerPresencePuller.d.ts","sourceRoot":"","sources":["../../src/core/PeerPresencePuller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,6FAA6F;AAC7F,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,sFAAsF;AACtF,MAAM,WAAW,YAAY;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"PeerPresencePuller.d.ts","sourceRoot":"","sources":["../../src/core/PeerPresencePuller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,6FAA6F;AAC7F,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,sFAAsF;AACtF,MAAM,WAAW,YAAY;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzE;;;;OAIG;IACH,iBAAiB,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;CACrE;AAED,yFAAyF;AACzF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,wEAAwE;IACxE,aAAa,EAAE,MAAM,CAAC;IACtB,yGAAyG;IACzG,SAAS,EAAE,MAAM,mBAAmB,EAAE,CAAC;IACvC;;;;OAIG;IACH,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IACpF,0GAA0G;IAC1G,eAAe,EAAE,CAAC,GAAG,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,oBAAoB,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACtG,6DAA6D;IAC7D,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,qEAAqE;IACrE,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAS7B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,CACpB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IACxC,2FAA2F;IAC3F,UAAU,CAAC,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,IAAI,CAAC;IAC5E;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjG;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,CACrB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,KACpD,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED,qBAAa,kBAAkB;IACjB,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAAD,CAAC,EAAE,sBAAsB;IAEtD;;;;OAIG;IACG,QAAQ,IAAI,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAmCjD;;;;;;;;OAQG;YACW,iBAAiB;CA4ChC"}
|
|
@@ -52,6 +52,18 @@ export class PeerPresencePuller {
|
|
|
52
52
|
// delta deps (i.e. replication.enabled === true). Otherwise a complete
|
|
53
53
|
// no-op (engine/transport stay dark). Never throws into the puller.
|
|
54
54
|
await this.driveJournalDelta(m.machineId, m.url, cap.journalAdvert);
|
|
55
|
+
if (cap.commitmentsAdvert && this.d.driveCommitmentsSync) {
|
|
56
|
+
try {
|
|
57
|
+
await this.d.driveCommitmentsSync(m.machineId, m.url, cap.commitmentsAdvert);
|
|
58
|
+
}
|
|
59
|
+
catch { /* @silent-fallback-ok: the puller's contract is NEVER to throw — a failed commitments pull retries on the next presence pass (COMMITMENTS-COHERENCE-SPEC §3.2) */
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
this.d.onPeerRecorded?.(m.machineId);
|
|
64
|
+
}
|
|
65
|
+
catch { /* @silent-fallback-ok: the puller's contract is NEVER to throw — a pending-pull re-arm failure must not break the presence pass (WORKING-SET-HANDOFF-SPEC §3.4) */
|
|
66
|
+
}
|
|
55
67
|
return m.machineId;
|
|
56
68
|
}));
|
|
57
69
|
const recorded = results.filter((r) => r !== null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PeerPresencePuller.js","sourceRoot":"","sources":["../../src/core/PeerPresencePuller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;
|
|
1
|
+
{"version":3,"file":"PeerPresencePuller.js","sourceRoot":"","sources":["../../src/core/PeerPresencePuller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PeerVisibilityGuard — P2.2 rider (WORKING-SET-HANDOFF-SPEC §3.6): improper
|
|
3
|
+
* revocations and silently-missing peers must surface LOUDLY.
|
|
4
|
+
*
|
|
5
|
+
* Earned 2026-06-06: the Mini was revoked with NO `revokedBy`/`revokeReason`
|
|
6
|
+
* (a write that bypassed `revokeMachine()`), making it invisible to the mesh
|
|
7
|
+
* for ~10 hours while it held a topic's entire overnight working set.
|
|
8
|
+
*
|
|
9
|
+
* Two detections, both HYGIENE SIGNALS (they detect sloppy state, not
|
|
10
|
+
* malicious action — populated fields are NOT authenticated and must never
|
|
11
|
+
* be read as "this revocation is legitimate"):
|
|
12
|
+
*
|
|
13
|
+
* 1. `detectImproperRevocations(registry)` — a PURE helper (deliberately NOT
|
|
14
|
+
* inside `loadRegistry()`, a hot dependency-free read called 41+ times
|
|
15
|
+
* per boot): an entry with `revokedAt` set but `revokedBy`/`revokeReason`
|
|
16
|
+
* missing. The consumer surfaces findings via the agent-health attention
|
|
17
|
+
* lane, deduped ACROSS boots keyed on the entry's `revokedAt` (a
|
|
18
|
+
* crash-loop cannot re-spam it).
|
|
19
|
+
*
|
|
20
|
+
* 2. Peer-disappearance notice — pool transitions from N≥2 online to fewer
|
|
21
|
+
* and stays past a 30-min grace → ONE agent-health notice naming the
|
|
22
|
+
* machine + last known reason, and — when pending-pulls reference it —
|
|
23
|
+
* the stranded topic working sets. Coalesced per machine per episode;
|
|
24
|
+
* FLAP-BOUNDED: 3 episodes for one machine in 24h collapses to a single
|
|
25
|
+
* "machine X is flapping" notice (Bounded Notification Surface). Clears
|
|
26
|
+
* silently on stable re-peer.
|
|
27
|
+
*/
|
|
28
|
+
import type { MachineRegistry } from './types.js';
|
|
29
|
+
export declare const DEFAULT_DISAPPEARANCE_GRACE_MS: number;
|
|
30
|
+
export declare const FLAP_EPISODE_LIMIT = 3;
|
|
31
|
+
export declare const FLAP_WINDOW_MS: number;
|
|
32
|
+
export interface ImproperRevocation {
|
|
33
|
+
machineId: string;
|
|
34
|
+
nickname?: string;
|
|
35
|
+
revokedAt: string;
|
|
36
|
+
missing: ('revokedBy' | 'revokeReason')[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* PURE: entries revoked without authorship/reason — the §3.6 hygiene signal.
|
|
40
|
+
* Never inside loadRegistry(); the consumer (boot/refresh path) calls this.
|
|
41
|
+
*/
|
|
42
|
+
export declare function detectImproperRevocations(registry: MachineRegistry): ImproperRevocation[];
|
|
43
|
+
export interface GuardNotice {
|
|
44
|
+
kind: 'improper-revocation' | 'peer-missing' | 'peer-flapping';
|
|
45
|
+
machineId: string;
|
|
46
|
+
title: string;
|
|
47
|
+
body: string;
|
|
48
|
+
}
|
|
49
|
+
export interface PeerVisibilityGuardConfig {
|
|
50
|
+
/** Absolute path to the agent's `.instar/` directory (durable dedupe state). */
|
|
51
|
+
stateDir: string;
|
|
52
|
+
selfMachineId: string;
|
|
53
|
+
/** Surface one notice (agent-health attention lane upstream). Never throws back. */
|
|
54
|
+
notify: (notice: GuardNotice) => void;
|
|
55
|
+
/** Topics with pending-pulls naming a machine (the stranded working sets). */
|
|
56
|
+
strandedTopicsFor?: (machineId: string) => Promise<number[]>;
|
|
57
|
+
graceMs?: number;
|
|
58
|
+
now?: () => Date;
|
|
59
|
+
logger?: (msg: string) => void;
|
|
60
|
+
}
|
|
61
|
+
export declare class PeerVisibilityGuard {
|
|
62
|
+
private readonly d;
|
|
63
|
+
private readonly stateFile;
|
|
64
|
+
private state;
|
|
65
|
+
/** In-memory per-episode tracking (a notice fires once per episode). */
|
|
66
|
+
private missing;
|
|
67
|
+
constructor(config: PeerVisibilityGuardConfig);
|
|
68
|
+
/**
|
|
69
|
+
* §3.6.1 — call on boot + registry refresh with the loaded registry.
|
|
70
|
+
* Surfaces each improper revocation ONCE across boots (keyed on revokedAt).
|
|
71
|
+
*/
|
|
72
|
+
checkRevocations(registry: MachineRegistry): ImproperRevocation[];
|
|
73
|
+
/**
|
|
74
|
+
* §3.6.2 — call on a presence cadence with the CURRENT online view.
|
|
75
|
+
* `expected` = machines that should be online (active, not revoked, not
|
|
76
|
+
* self); `online` = machines recorded this pass.
|
|
77
|
+
*/
|
|
78
|
+
checkDisappearances(expected: string[], online: Set<string>): Promise<void>;
|
|
79
|
+
private loadState;
|
|
80
|
+
private persist;
|
|
81
|
+
private safeNotify;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=PeerVisibilityGuard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PeerVisibilityGuard.d.ts","sourceRoot":"","sources":["../../src/core/PeerVisibilityGuard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAwB,MAAM,YAAY,CAAC;AAExE,eAAO,MAAM,8BAA8B,QAAiB,CAAC;AAC7D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,eAAO,MAAM,cAAc,QAAsB,CAAC;AAElD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,CAAC,WAAW,GAAG,cAAc,CAAC,EAAE,CAAC;CAC3C;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,eAAe,GAAG,kBAAkB,EAAE,CAkBzF;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,qBAAqB,GAAG,cAAc,GAAG,eAAe,CAAC;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,yBAAyB;IACxC,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;IACtC,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAiBD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,CAAC,CAA4B;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,KAAK,CAAgC;IAC7C,wEAAwE;IACxE,OAAO,CAAC,OAAO,CAAqC;gBAExC,MAAM,EAAE,yBAAyB;IAK7C;;;OAGG;IACH,gBAAgB,CAAC,QAAQ,EAAE,eAAe,GAAG,kBAAkB,EAAE;IAsBjE;;;;OAIG;IACG,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAuEjF,OAAO,CAAC,SAAS;IAmBjB,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,UAAU;CAMnB"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PeerVisibilityGuard — P2.2 rider (WORKING-SET-HANDOFF-SPEC §3.6): improper
|
|
3
|
+
* revocations and silently-missing peers must surface LOUDLY.
|
|
4
|
+
*
|
|
5
|
+
* Earned 2026-06-06: the Mini was revoked with NO `revokedBy`/`revokeReason`
|
|
6
|
+
* (a write that bypassed `revokeMachine()`), making it invisible to the mesh
|
|
7
|
+
* for ~10 hours while it held a topic's entire overnight working set.
|
|
8
|
+
*
|
|
9
|
+
* Two detections, both HYGIENE SIGNALS (they detect sloppy state, not
|
|
10
|
+
* malicious action — populated fields are NOT authenticated and must never
|
|
11
|
+
* be read as "this revocation is legitimate"):
|
|
12
|
+
*
|
|
13
|
+
* 1. `detectImproperRevocations(registry)` — a PURE helper (deliberately NOT
|
|
14
|
+
* inside `loadRegistry()`, a hot dependency-free read called 41+ times
|
|
15
|
+
* per boot): an entry with `revokedAt` set but `revokedBy`/`revokeReason`
|
|
16
|
+
* missing. The consumer surfaces findings via the agent-health attention
|
|
17
|
+
* lane, deduped ACROSS boots keyed on the entry's `revokedAt` (a
|
|
18
|
+
* crash-loop cannot re-spam it).
|
|
19
|
+
*
|
|
20
|
+
* 2. Peer-disappearance notice — pool transitions from N≥2 online to fewer
|
|
21
|
+
* and stays past a 30-min grace → ONE agent-health notice naming the
|
|
22
|
+
* machine + last known reason, and — when pending-pulls reference it —
|
|
23
|
+
* the stranded topic working sets. Coalesced per machine per episode;
|
|
24
|
+
* FLAP-BOUNDED: 3 episodes for one machine in 24h collapses to a single
|
|
25
|
+
* "machine X is flapping" notice (Bounded Notification Surface). Clears
|
|
26
|
+
* silently on stable re-peer.
|
|
27
|
+
*/
|
|
28
|
+
import fs from 'node:fs';
|
|
29
|
+
import path from 'node:path';
|
|
30
|
+
export const DEFAULT_DISAPPEARANCE_GRACE_MS = 30 * 60 * 1000;
|
|
31
|
+
export const FLAP_EPISODE_LIMIT = 3;
|
|
32
|
+
export const FLAP_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
33
|
+
/**
|
|
34
|
+
* PURE: entries revoked without authorship/reason — the §3.6 hygiene signal.
|
|
35
|
+
* Never inside loadRegistry(); the consumer (boot/refresh path) calls this.
|
|
36
|
+
*/
|
|
37
|
+
export function detectImproperRevocations(registry) {
|
|
38
|
+
const out = [];
|
|
39
|
+
for (const [machineId, entry] of Object.entries(registry.machines ?? {})) {
|
|
40
|
+
const e = entry;
|
|
41
|
+
if (!e.revokedAt)
|
|
42
|
+
continue;
|
|
43
|
+
const missing = [];
|
|
44
|
+
if (!e.revokedBy)
|
|
45
|
+
missing.push('revokedBy');
|
|
46
|
+
if (!e.revokeReason)
|
|
47
|
+
missing.push('revokeReason');
|
|
48
|
+
if (missing.length) {
|
|
49
|
+
out.push({
|
|
50
|
+
machineId,
|
|
51
|
+
...(e.nickname ? { nickname: e.nickname } : {}),
|
|
52
|
+
revokedAt: e.revokedAt,
|
|
53
|
+
missing,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
export class PeerVisibilityGuard {
|
|
60
|
+
d;
|
|
61
|
+
stateFile;
|
|
62
|
+
state = null;
|
|
63
|
+
/** In-memory per-episode tracking (a notice fires once per episode). */
|
|
64
|
+
missing = new Map();
|
|
65
|
+
constructor(config) {
|
|
66
|
+
this.d = config;
|
|
67
|
+
this.stateFile = path.join(config.stateDir, 'state', 'coherence-journal', 'visibility-guard.json');
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* §3.6.1 — call on boot + registry refresh with the loaded registry.
|
|
71
|
+
* Surfaces each improper revocation ONCE across boots (keyed on revokedAt).
|
|
72
|
+
*/
|
|
73
|
+
checkRevocations(registry) {
|
|
74
|
+
const found = detectImproperRevocations(registry);
|
|
75
|
+
const st = this.loadState();
|
|
76
|
+
const fresh = found.filter((f) => !st.surfacedRevocations.includes(f.revokedAt));
|
|
77
|
+
for (const f of fresh) {
|
|
78
|
+
st.surfacedRevocations.push(f.revokedAt);
|
|
79
|
+
this.safeNotify({
|
|
80
|
+
kind: 'improper-revocation',
|
|
81
|
+
machineId: f.machineId,
|
|
82
|
+
title: `Machine ${f.nickname ?? f.machineId} was revoked without ${f.missing.join('/')}`,
|
|
83
|
+
body: `${f.nickname ?? f.machineId} is marked revoked (at ${f.revokedAt}) but the revocation ` +
|
|
84
|
+
`carries no ${f.missing.join(' or ')} — it did not go through revokeMachine(). ` +
|
|
85
|
+
`HYGIENE SIGNAL ONLY: this detects a sloppy revocation, not a malicious one (and a ` +
|
|
86
|
+
`populated field would not prove legitimacy either). If this machine should be in the ` +
|
|
87
|
+
`pool, un-revoke it; its working sets are unreachable until then.`,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (fresh.length)
|
|
91
|
+
this.persist(st);
|
|
92
|
+
return found;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* §3.6.2 — call on a presence cadence with the CURRENT online view.
|
|
96
|
+
* `expected` = machines that should be online (active, not revoked, not
|
|
97
|
+
* self); `online` = machines recorded this pass.
|
|
98
|
+
*/
|
|
99
|
+
async checkDisappearances(expected, online) {
|
|
100
|
+
const nowMs = (this.d.now?.() ?? new Date()).getTime();
|
|
101
|
+
const grace = this.d.graceMs ?? DEFAULT_DISAPPEARANCE_GRACE_MS;
|
|
102
|
+
const st = this.loadState();
|
|
103
|
+
let dirty = false;
|
|
104
|
+
for (const machineId of expected) {
|
|
105
|
+
if (machineId === this.d.selfMachineId)
|
|
106
|
+
continue;
|
|
107
|
+
if (online.has(machineId)) {
|
|
108
|
+
// Stable re-peer → episode closes SILENTLY (§3.6.2).
|
|
109
|
+
if (this.missing.delete(machineId))
|
|
110
|
+
this.d.logger?.(`peer ${machineId} re-peered`);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
let tracker = this.missing.get(machineId);
|
|
114
|
+
if (!tracker) {
|
|
115
|
+
tracker = { missingSinceMs: nowMs, noticed: false };
|
|
116
|
+
this.missing.set(machineId, tracker);
|
|
117
|
+
}
|
|
118
|
+
if (tracker.noticed || nowMs - tracker.missingSinceMs < grace)
|
|
119
|
+
continue;
|
|
120
|
+
// Episode confirmed past grace — flap accounting first.
|
|
121
|
+
const episodes = (st.episodes[machineId] ?? []).filter((iso) => nowMs - new Date(iso).getTime() < FLAP_WINDOW_MS);
|
|
122
|
+
episodes.push(new Date(nowMs).toISOString());
|
|
123
|
+
st.episodes[machineId] = episodes;
|
|
124
|
+
dirty = true;
|
|
125
|
+
tracker.noticed = true;
|
|
126
|
+
if (episodes.length > FLAP_EPISODE_LIMIT) {
|
|
127
|
+
if (!st.flapNotified[machineId]) {
|
|
128
|
+
st.flapNotified[machineId] = true;
|
|
129
|
+
this.safeNotify({
|
|
130
|
+
kind: 'peer-flapping',
|
|
131
|
+
machineId,
|
|
132
|
+
title: `Machine ${machineId} is flapping`,
|
|
133
|
+
body: `${machineId} has dropped off the mesh ${episodes.length} times in 24h. ` +
|
|
134
|
+
`Collapsing to this single notice — no further per-episode messages until ` +
|
|
135
|
+
`it stabilizes (Bounded Notification Surface).`,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
// Flap-notified machines that stabilized under the limit get re-armed.
|
|
141
|
+
if (st.flapNotified[machineId] && episodes.length <= FLAP_EPISODE_LIMIT) {
|
|
142
|
+
delete st.flapNotified[machineId];
|
|
143
|
+
}
|
|
144
|
+
let stranded = [];
|
|
145
|
+
try {
|
|
146
|
+
stranded = (await this.d.strandedTopicsFor?.(machineId)) ?? [];
|
|
147
|
+
}
|
|
148
|
+
catch { /* @silent-fallback-ok: the stranded-topics enrichment is best-effort; the disappearance notice itself must still fire (WORKING-SET-HANDOFF-SPEC §3.6) */
|
|
149
|
+
}
|
|
150
|
+
const strandedLine = stranded.length
|
|
151
|
+
? ` Topic working set(s) stranded on it: ${stranded.join(', ')} — recover by bringing it back / un-revoking; the pending fetch re-fires automatically on its return.`
|
|
152
|
+
: '';
|
|
153
|
+
this.safeNotify({
|
|
154
|
+
kind: 'peer-missing',
|
|
155
|
+
machineId,
|
|
156
|
+
title: `Machine ${machineId} has been unreachable for 30+ minutes`,
|
|
157
|
+
body: `${machineId} should be in the pool but has not answered the presence pull past the ` +
|
|
158
|
+
`grace window.${strandedLine}`,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
if (dirty)
|
|
162
|
+
this.persist(st);
|
|
163
|
+
}
|
|
164
|
+
// ---- durable dedupe state -------------------------------------------------
|
|
165
|
+
loadState() {
|
|
166
|
+
if (this.state)
|
|
167
|
+
return this.state;
|
|
168
|
+
try {
|
|
169
|
+
const parsed = JSON.parse(fs.readFileSync(this.stateFile, 'utf-8'));
|
|
170
|
+
if (parsed?.version === 1) {
|
|
171
|
+
this.state = {
|
|
172
|
+
version: 1,
|
|
173
|
+
surfacedRevocations: Array.isArray(parsed.surfacedRevocations) ? parsed.surfacedRevocations : [],
|
|
174
|
+
episodes: parsed.episodes && typeof parsed.episodes === 'object' ? parsed.episodes : {},
|
|
175
|
+
flapNotified: parsed.flapNotified && typeof parsed.flapNotified === 'object' ? parsed.flapNotified : {},
|
|
176
|
+
};
|
|
177
|
+
return this.state;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch { /* @silent-fallback-ok: absent/corrupt guard state only weakens notice dedupe (worst case one repeat notice) — never a failure (WORKING-SET-HANDOFF-SPEC §3.6) */
|
|
181
|
+
}
|
|
182
|
+
this.state = { version: 1, surfacedRevocations: [], episodes: {}, flapNotified: {} };
|
|
183
|
+
return this.state;
|
|
184
|
+
}
|
|
185
|
+
persist(st) {
|
|
186
|
+
try {
|
|
187
|
+
fs.mkdirSync(path.dirname(this.stateFile), { recursive: true });
|
|
188
|
+
const tmp = `${this.stateFile}.tmp-${process.pid}`;
|
|
189
|
+
fs.writeFileSync(tmp, JSON.stringify(st, null, 2));
|
|
190
|
+
fs.renameSync(tmp, this.stateFile);
|
|
191
|
+
}
|
|
192
|
+
catch (e) { /* @silent-fallback-ok: a failed dedupe persist risks one repeat notice after restart, never a lost detection (WORKING-SET-HANDOFF-SPEC §3.6) */
|
|
193
|
+
this.d.logger?.(`guard state persist failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
safeNotify(notice) {
|
|
197
|
+
try {
|
|
198
|
+
this.d.notify(notice);
|
|
199
|
+
}
|
|
200
|
+
catch { /* @silent-fallback-ok: a notify-consumer failure must never break the guard's detection loop (WORKING-SET-HANDOFF-SPEC §3.6) */
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=PeerVisibilityGuard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PeerVisibilityGuard.js","sourceRoot":"","sources":["../../src/core/PeerVisibilityGuard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PendingPullLedger — P2.2 of multi-machine coherence: the durable record of
|
|
3
|
+
* working-set pulls that could not complete because the producer was
|
|
4
|
+
* offline / unreachable / revoked / mid-run. The EXO case, solved: the
|
|
5
|
+
* request survives restarts and re-fires the moment the peer returns.
|
|
6
|
+
*
|
|
7
|
+
* Spec: docs/specs/WORKING-SET-HANDOFF-SPEC.md §3.4.
|
|
8
|
+
*
|
|
9
|
+
* SINGLE-WRITER DISCIPLINE (the topic-flood-#3 lesson, applied at birth):
|
|
10
|
+
* SIX mutators can overlap on one tick (onAccepted scheduler, reappearance
|
|
11
|
+
* re-arm, run-stopped re-arm, attempt/breaker update, reflex route, TTL
|
|
12
|
+
* sweep). ALL mutations route through ONE serialized `mutate(fn)` funnel —
|
|
13
|
+
* an in-process async queue, so read-modify-write never interleaves — with
|
|
14
|
+
* temp-file + atomic-rename persistence.
|
|
15
|
+
*
|
|
16
|
+
* PARSE-FAILURE POSTURE (the flood's second root): a corrupt/unparseable
|
|
17
|
+
* ledger is NEVER read as "no pending pulls" — the file is quarantined
|
|
18
|
+
* aside (`.corrupt-<ts>`), ONE notice fires through the injected
|
|
19
|
+
* `onCorrupt` seam ("pending-pull ledger unreadable — stranded-recovery
|
|
20
|
+
* records may be lost"), and a fresh ledger starts.
|
|
21
|
+
*
|
|
22
|
+
* Store path: `state/coherence-journal/pending-pulls.json` — registered in
|
|
23
|
+
* the State-Coherence Registry (machine-local; the records describe THIS
|
|
24
|
+
* machine's outstanding fetches).
|
|
25
|
+
*/
|
|
26
|
+
export declare const PENDING_PULLS_FILENAME = "pending-pulls.json";
|
|
27
|
+
export declare const DEFAULT_PENDING_PULL_TTL_DAYS = 7;
|
|
28
|
+
/** Per-record attempt cap — the (peer,topic,epoch) breaker (§3.4). */
|
|
29
|
+
export declare const DEFAULT_ATTEMPT_CAP = 6;
|
|
30
|
+
export type PendingPullReason = 'peer-offline' | 'peer-unreachable' | 'peer-revoked' | 'live-source' | 'busy-exhausted';
|
|
31
|
+
export interface PendingPullRecord {
|
|
32
|
+
topic: number;
|
|
33
|
+
epoch: number;
|
|
34
|
+
nominee: string;
|
|
35
|
+
reason: PendingPullReason;
|
|
36
|
+
createdAt: string;
|
|
37
|
+
attempts: number;
|
|
38
|
+
lastAttemptAt: string | null;
|
|
39
|
+
}
|
|
40
|
+
export interface PendingPullLedgerConfig {
|
|
41
|
+
/** Absolute path to the agent's `.instar/` directory. */
|
|
42
|
+
stateDir: string;
|
|
43
|
+
ttlDays?: number;
|
|
44
|
+
attemptCap?: number;
|
|
45
|
+
now?: () => Date;
|
|
46
|
+
/**
|
|
47
|
+
* Fired ONCE per corrupt-quarantine episode (agent-health lane upstream).
|
|
48
|
+
* Never throws into the ledger.
|
|
49
|
+
*/
|
|
50
|
+
onCorrupt?: (quarantinedPath: string) => void;
|
|
51
|
+
/**
|
|
52
|
+
* Fired once per record on TTL expiry — "topic T's working set on
|
|
53
|
+
* <machine> was never recovered" (§3.4). Never throws into the ledger.
|
|
54
|
+
*/
|
|
55
|
+
onExpired?: (record: PendingPullRecord) => void;
|
|
56
|
+
logger?: (msg: string) => void;
|
|
57
|
+
}
|
|
58
|
+
export declare class PendingPullLedger {
|
|
59
|
+
private readonly file;
|
|
60
|
+
private readonly ttlMs;
|
|
61
|
+
private readonly attemptCap;
|
|
62
|
+
private readonly now;
|
|
63
|
+
private readonly onCorrupt?;
|
|
64
|
+
private readonly onExpired?;
|
|
65
|
+
private readonly logger;
|
|
66
|
+
/** The serialized mutate() funnel — read-modify-write never interleaves. */
|
|
67
|
+
private queue;
|
|
68
|
+
/** In-memory truth between mutations (loaded once, kept current). */
|
|
69
|
+
private records;
|
|
70
|
+
private corruptNotified;
|
|
71
|
+
constructor(config: PendingPullLedgerConfig);
|
|
72
|
+
/**
|
|
73
|
+
* File (or refresh) a pending pull. Idempotent on (topic, epoch, nominee):
|
|
74
|
+
* an existing record keeps its createdAt/attempts; only `reason` refreshes.
|
|
75
|
+
*/
|
|
76
|
+
file_(rec: {
|
|
77
|
+
topic: number;
|
|
78
|
+
epoch: number;
|
|
79
|
+
nominee: string;
|
|
80
|
+
reason: PendingPullReason;
|
|
81
|
+
}): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Record a GENUINE failed attempt (offline / unreachable / refused /
|
|
84
|
+
* verify-failed). `busy` responses MUST NOT come through here — busy is
|
|
85
|
+
* retry-without-penalty (§3.2); a throttled drain must never exhaust the
|
|
86
|
+
* very records it exists to recover.
|
|
87
|
+
*/
|
|
88
|
+
recordAttempt(topic: number, epoch: number, nominee: string): Promise<void>;
|
|
89
|
+
/** A completed pull clears its record. */
|
|
90
|
+
clear(topic: number, epoch: number, nominee: string): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Supersession (§3.4): a newer epoch for the topic clears ALL records for
|
|
93
|
+
* that topic with `epoch < newEpoch`, across all nominees — a partial clear
|
|
94
|
+
* must never strand a sibling record.
|
|
95
|
+
*/
|
|
96
|
+
supersede(topic: number, newEpoch: number): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* TTL sweep: expire records older than ttlDays — each surfaced ONCE via
|
|
99
|
+
* onExpired ("never recovered"), then removed. Run on a slow cadence by the
|
|
100
|
+
* caller (it is one of the six mutators, so it rides the funnel too).
|
|
101
|
+
* Returns the expired records.
|
|
102
|
+
*/
|
|
103
|
+
sweepExpired(): Promise<PendingPullRecord[]>;
|
|
104
|
+
/**
|
|
105
|
+
* The records eligible to re-fire for a returning peer — the staggered
|
|
106
|
+
* drain reads this (most-recent-epoch-first; the drain itself enforces
|
|
107
|
+
* rearmConcurrency). Records at/over the attempt cap are excluded (the
|
|
108
|
+
* breaker; a NEW epoch files a NEW record, so an old exhausted breaker
|
|
109
|
+
* never suppresses a fresh, warranted pull).
|
|
110
|
+
*/
|
|
111
|
+
pendingForPeer(nominee: string): Promise<PendingPullRecord[]>;
|
|
112
|
+
/** All records for a topic (the run-stopped re-arm + reflex route read this). */
|
|
113
|
+
pendingForTopic(topic: number): Promise<PendingPullRecord[]>;
|
|
114
|
+
/** Every record, including breaker-exhausted ones (observability). */
|
|
115
|
+
all(): Promise<PendingPullRecord[]>;
|
|
116
|
+
/**
|
|
117
|
+
* Serialize a read-modify-write. The fn receives the current records array
|
|
118
|
+
* (mutable) and returns the next one; persistence is temp-file + atomic
|
|
119
|
+
* rename. Errors persist nothing and propagate to THIS caller only — the
|
|
120
|
+
* queue itself never wedges.
|
|
121
|
+
*/
|
|
122
|
+
private mutate;
|
|
123
|
+
private read;
|
|
124
|
+
/** Load from memory or disk. Corrupt disk → quarantine + notice + fresh. */
|
|
125
|
+
private readUnqueued;
|
|
126
|
+
private quarantineCorrupt;
|
|
127
|
+
private persist;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=PendingPullLedger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PendingPullLedger.d.ts","sourceRoot":"","sources":["../../src/core/PendingPullLedger.ts"],"names":[],"mappings":"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"}
|