@mneme-ai/core 2.13.1 → 2.14.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/agent_manifest.d.ts +1 -1
- package/dist/agent_manifest.d.ts.map +1 -1
- package/dist/agent_manifest.js +22 -1
- package/dist/agent_manifest.js.map +1 -1
- package/dist/bounty/bounty.test.d.ts +2 -0
- package/dist/bounty/bounty.test.d.ts.map +1 -0
- package/dist/bounty/bounty.test.js +125 -0
- package/dist/bounty/bounty.test.js.map +1 -0
- package/dist/bounty/index.d.ts +142 -0
- package/dist/bounty/index.d.ts.map +1 -0
- package/dist/bounty/index.js +180 -0
- package/dist/bounty/index.js.map +1 -0
- package/dist/cosmic/aurelian_v214.test.d.ts +9 -0
- package/dist/cosmic/aurelian_v214.test.d.ts.map +1 -0
- package/dist/cosmic/aurelian_v214.test.js +84 -0
- package/dist/cosmic/aurelian_v214.test.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/infra_brain/index.d.ts +148 -0
- package/dist/infra_brain/index.d.ts.map +1 -0
- package/dist/infra_brain/index.js +243 -0
- package/dist/infra_brain/index.js.map +1 -0
- package/dist/infra_brain/infra_brain.test.d.ts +2 -0
- package/dist/infra_brain/infra_brain.test.d.ts.map +1 -0
- package/dist/infra_brain/infra_brain.test.js +154 -0
- package/dist/infra_brain/infra_brain.test.js.map +1 -0
- package/dist/kill_switch/index.d.ts +168 -0
- package/dist/kill_switch/index.d.ts.map +1 -0
- package/dist/kill_switch/index.js +285 -0
- package/dist/kill_switch/index.js.map +1 -0
- package/dist/kill_switch/kill_switch.test.d.ts +2 -0
- package/dist/kill_switch/kill_switch.test.d.ts.map +1 -0
- package/dist/kill_switch/kill_switch.test.js +154 -0
- package/dist/kill_switch/kill_switch.test.js.map +1 -0
- package/dist/project_soul/index.d.ts +123 -0
- package/dist/project_soul/index.d.ts.map +1 -0
- package/dist/project_soul/index.js +241 -0
- package/dist/project_soul/index.js.map +1 -0
- package/dist/project_soul/project_soul.test.d.ts +2 -0
- package/dist/project_soul/project_soul.test.d.ts.map +1 -0
- package/dist/project_soul/project_soul.test.js +123 -0
- package/dist/project_soul/project_soul.test.js.map +1 -0
- package/dist/replica/index.d.ts +105 -0
- package/dist/replica/index.d.ts.map +1 -0
- package/dist/replica/index.js +206 -0
- package/dist/replica/index.js.map +1 -0
- package/dist/replica/replica.test.d.ts +2 -0
- package/dist/replica/replica.test.d.ts.map +1 -0
- package/dist/replica/replica.test.js +102 -0
- package/dist/replica/replica.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.14.0 — INFRA AS AI
|
|
3
|
+
*
|
|
4
|
+
* "Every server, container, database, and CI job is an agent with
|
|
5
|
+
* memory. They observe their own behaviour, gossip with peers, and
|
|
6
|
+
* surface insights when humans aren't watching. Mneme = the brain
|
|
7
|
+
* inside each host."
|
|
8
|
+
*
|
|
9
|
+
* Two layers, both work standalone:
|
|
10
|
+
*
|
|
11
|
+
* 1. HOST BRAIN — a per-host local store of observations. Things
|
|
12
|
+
* noticed: latency outliers, error spikes, deploy events, cron
|
|
13
|
+
* misfires. Each observation is HMAC-signed + de-duplicated.
|
|
14
|
+
*
|
|
15
|
+
* 2. GOSSIP PRIMITIVE — peer-to-peer exchange of redacted summaries
|
|
16
|
+
* (HOST BRAIN exports a `digest` that other hosts can ingest). No
|
|
17
|
+
* central server required. Anti-entropy through periodic digest
|
|
18
|
+
* swaps.
|
|
19
|
+
*
|
|
20
|
+
* Killer property: the network of host brains becomes a *distributed
|
|
21
|
+
* memory* that survives any single failure. Like git's commit graph but
|
|
22
|
+
* for infrastructure observations.
|
|
23
|
+
*
|
|
24
|
+
* Storage: `.mneme/infra/observations.jsonl` — local-first, append-only.
|
|
25
|
+
* Digests are HMAC-signed for tamper-evidence on the wire.
|
|
26
|
+
*
|
|
27
|
+
* Wisdom: this is the seed. Full distributed gossip protocol (mesh,
|
|
28
|
+
* pull-sync, conflict resolution) is Phase 2 — once enough hosts run
|
|
29
|
+
* the seed. Phase 1 is "personal observability that compounds".
|
|
30
|
+
*/
|
|
31
|
+
import { createHmac, randomBytes } from "node:crypto";
|
|
32
|
+
import { readFileSync, existsSync, mkdirSync, appendFileSync } from "node:fs";
|
|
33
|
+
import { join, resolve, isAbsolute } from "node:path";
|
|
34
|
+
import { hostname } from "node:os";
|
|
35
|
+
const PROTOCOL_VERSION = 1;
|
|
36
|
+
function canon(v) {
|
|
37
|
+
if (v === null || typeof v !== "object")
|
|
38
|
+
return JSON.stringify(v);
|
|
39
|
+
if (Array.isArray(v))
|
|
40
|
+
return "[" + v.map(canon).join(",") + "]";
|
|
41
|
+
const keys = Object.keys(v).sort();
|
|
42
|
+
return "{" + keys.map((k) => JSON.stringify(k) + ":" + canon(v[k])).join(",") + "}";
|
|
43
|
+
}
|
|
44
|
+
function defaultSecret() {
|
|
45
|
+
return process.env["MNEME_INFRA_SECRET"] || `mneme-infra-default-v${PROTOCOL_VERSION}`;
|
|
46
|
+
}
|
|
47
|
+
function infraDir(repoDir) {
|
|
48
|
+
const root = repoDir ? (isAbsolute(repoDir) ? repoDir : resolve(repoDir)) : process.cwd();
|
|
49
|
+
const dir = join(root, ".mneme", "infra");
|
|
50
|
+
if (!existsSync(dir))
|
|
51
|
+
mkdirSync(dir, { recursive: true });
|
|
52
|
+
return dir;
|
|
53
|
+
}
|
|
54
|
+
function observationsPath(repoDir) {
|
|
55
|
+
return join(infraDir(repoDir), "observations.jsonl");
|
|
56
|
+
}
|
|
57
|
+
function readAll(path) {
|
|
58
|
+
if (!existsSync(path))
|
|
59
|
+
return [];
|
|
60
|
+
const lines = readFileSync(path, "utf8").split(/\r?\n/).filter((l) => l.trim().length > 0);
|
|
61
|
+
const out = [];
|
|
62
|
+
for (const l of lines) {
|
|
63
|
+
try {
|
|
64
|
+
out.push(JSON.parse(l));
|
|
65
|
+
}
|
|
66
|
+
catch { }
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
function signObs(body, secret) {
|
|
71
|
+
return createHmac("sha256", secret).update(canon(body)).digest("hex");
|
|
72
|
+
}
|
|
73
|
+
export function recordObservation(input) {
|
|
74
|
+
const path = observationsPath(input.repoDir);
|
|
75
|
+
const noSig = {
|
|
76
|
+
v: PROTOCOL_VERSION,
|
|
77
|
+
id: "o-" + randomBytes(6).toString("hex"),
|
|
78
|
+
ts: new Date().toISOString(),
|
|
79
|
+
host: input.host ?? hostname(),
|
|
80
|
+
kind: input.kind,
|
|
81
|
+
subject: input.subject.slice(0, 200),
|
|
82
|
+
detail: input.detail.slice(0, 1000),
|
|
83
|
+
...(input.metric ? { metric: input.metric } : {}),
|
|
84
|
+
...(input.tags ? { tags: input.tags } : {}),
|
|
85
|
+
};
|
|
86
|
+
const obs = { ...noSig, sig: signObs(noSig, input.secret ?? defaultSecret()) };
|
|
87
|
+
appendFileSync(path, JSON.stringify(obs) + "\n");
|
|
88
|
+
return obs;
|
|
89
|
+
}
|
|
90
|
+
/** Verify a single observation came from the trusted secret holder. */
|
|
91
|
+
export function verifyObservation(obs, secret) {
|
|
92
|
+
const { sig: claimed, ...body } = obs;
|
|
93
|
+
const expected = signObs(body, secret ?? defaultSecret());
|
|
94
|
+
return expected === claimed;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Pattern detector — finds recurring (kind, subject) tuples and
|
|
98
|
+
* estimates their recurrence window.
|
|
99
|
+
*/
|
|
100
|
+
export function detectPatterns(opts = {}) {
|
|
101
|
+
const all = readAll(observationsPath(opts.repoDir));
|
|
102
|
+
const min = opts.minOccurrences ?? 2;
|
|
103
|
+
const groups = new Map();
|
|
104
|
+
for (const o of all) {
|
|
105
|
+
const key = `${o.kind}|${o.subject}`;
|
|
106
|
+
if (!groups.has(key))
|
|
107
|
+
groups.set(key, []);
|
|
108
|
+
groups.get(key).push(o);
|
|
109
|
+
}
|
|
110
|
+
const results = [];
|
|
111
|
+
for (const [key, obs] of groups) {
|
|
112
|
+
if (obs.length < min)
|
|
113
|
+
continue;
|
|
114
|
+
obs.sort((a, b) => a.ts.localeCompare(b.ts));
|
|
115
|
+
const window = estimateWindow(obs);
|
|
116
|
+
results.push({
|
|
117
|
+
count: obs.length,
|
|
118
|
+
kind: obs[0].kind,
|
|
119
|
+
subject: obs[0].subject,
|
|
120
|
+
firstSeen: obs[0].ts,
|
|
121
|
+
lastSeen: obs[obs.length - 1].ts,
|
|
122
|
+
...(window ? { window } : {}),
|
|
123
|
+
examples: obs.slice(-3).map((o) => o.detail),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return results.sort((a, b) => b.count - a.count);
|
|
127
|
+
}
|
|
128
|
+
/** Estimate a recurring time-of-day window if observations cluster. */
|
|
129
|
+
function estimateWindow(obs) {
|
|
130
|
+
if (obs.length < 3)
|
|
131
|
+
return undefined;
|
|
132
|
+
const hours = obs.map((o) => new Date(o.ts).getUTCHours()).filter((h) => Number.isFinite(h));
|
|
133
|
+
if (hours.length === 0)
|
|
134
|
+
return undefined;
|
|
135
|
+
const mean = hours.reduce((a, b) => a + b, 0) / hours.length;
|
|
136
|
+
const variance = hours.reduce((a, b) => a + (b - mean) ** 2, 0) / hours.length;
|
|
137
|
+
if (variance > 4)
|
|
138
|
+
return undefined; // too spread to call a "window"
|
|
139
|
+
// Day-of-week analysis
|
|
140
|
+
const dows = obs.map((o) => new Date(o.ts).getUTCDay());
|
|
141
|
+
const dowCounts = new Map();
|
|
142
|
+
for (const d of dows)
|
|
143
|
+
dowCounts.set(d, (dowCounts.get(d) ?? 0) + 1);
|
|
144
|
+
const dominant = Array.from(dowCounts.entries()).sort((a, b) => b[1] - a[1])[0];
|
|
145
|
+
const dowFreq = dominant ? dominant[1] / dows.length : 0;
|
|
146
|
+
const dowNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
147
|
+
const dowPart = dowFreq > 0.6 ? `${dowNames[dominant[0]]} ` : "";
|
|
148
|
+
return `${dowPart}${String(Math.round(mean)).padStart(2, "0")}:00 UTC ± ${Math.ceil(Math.sqrt(variance))}h`;
|
|
149
|
+
}
|
|
150
|
+
/** Produce a redacted, HMAC-signed digest suitable for gossip exchange. */
|
|
151
|
+
export function exportDigest(opts = {}) {
|
|
152
|
+
const all = readAll(observationsPath(opts.repoDir));
|
|
153
|
+
const patterns = detectPatterns(opts);
|
|
154
|
+
const body = {
|
|
155
|
+
v: PROTOCOL_VERSION,
|
|
156
|
+
host: hostname(),
|
|
157
|
+
generatedAt: new Date().toISOString(),
|
|
158
|
+
observationCount: all.length,
|
|
159
|
+
patterns,
|
|
160
|
+
};
|
|
161
|
+
const sig = createHmac("sha256", opts.secret ?? defaultSecret()).update(canon(body)).digest("hex");
|
|
162
|
+
return { ...body, sig };
|
|
163
|
+
}
|
|
164
|
+
/** Verify a peer's digest with the shared secret before trusting it. */
|
|
165
|
+
export function verifyDigest(d, secret) {
|
|
166
|
+
const { sig: claimed, ...body } = d;
|
|
167
|
+
const expected = createHmac("sha256", secret ?? defaultSecret()).update(canon(body)).digest("hex");
|
|
168
|
+
return expected === claimed;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Ingest a peer's digest. Verified peers contribute pattern observations
|
|
172
|
+
* (NOT raw events — we share aggregates only for privacy + bandwidth).
|
|
173
|
+
* Each learned pattern is appended as a synthetic observation tagged
|
|
174
|
+
* `from:<peer-host>` for traceability.
|
|
175
|
+
*/
|
|
176
|
+
export function ingestDigest(d, opts = {}) {
|
|
177
|
+
if (!verifyDigest(d, opts.secret)) {
|
|
178
|
+
return { accepted: false, reason: "digest sig mismatch — unverified peer", patternsLearned: 0 };
|
|
179
|
+
}
|
|
180
|
+
let learned = 0;
|
|
181
|
+
for (const p of d.patterns) {
|
|
182
|
+
if (p.count < 2)
|
|
183
|
+
continue;
|
|
184
|
+
recordObservation({
|
|
185
|
+
kind: p.kind,
|
|
186
|
+
subject: p.subject,
|
|
187
|
+
detail: `[peer:${d.host}] saw ${p.count} occurrences${p.window ? ` at ${p.window}` : ""}. Examples: ${p.examples.join(" | ").slice(0, 400)}`,
|
|
188
|
+
tags: ["gossip", `from:${d.host}`],
|
|
189
|
+
repoDir: opts.repoDir,
|
|
190
|
+
secret: opts.secret,
|
|
191
|
+
});
|
|
192
|
+
learned++;
|
|
193
|
+
}
|
|
194
|
+
return { accepted: true, patternsLearned: learned };
|
|
195
|
+
}
|
|
196
|
+
const STOP = new Set(["the", "a", "an", "is", "are", "in", "on", "of", "to", "for", "and", "or", "but", "with", "be", "this", "that", "at"]);
|
|
197
|
+
function tokenize(text) {
|
|
198
|
+
return new Set(text.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 3 && !STOP.has(t)));
|
|
199
|
+
}
|
|
200
|
+
function jaccard(a, b) {
|
|
201
|
+
if (a.size === 0 && b.size === 0)
|
|
202
|
+
return 0;
|
|
203
|
+
let inter = 0;
|
|
204
|
+
for (const x of a)
|
|
205
|
+
if (b.has(x))
|
|
206
|
+
inter++;
|
|
207
|
+
const union = a.size + b.size - inter;
|
|
208
|
+
return union === 0 ? 0 : inter / union;
|
|
209
|
+
}
|
|
210
|
+
export function diagnose(input) {
|
|
211
|
+
const all = readAll(observationsPath(input.repoDir));
|
|
212
|
+
const qTokens = tokenize(`${input.subject} ${input.detail}`);
|
|
213
|
+
const scored = all.map((o) => ({
|
|
214
|
+
o, sim: jaccard(qTokens, tokenize(`${o.subject} ${o.detail}`)),
|
|
215
|
+
})).filter((s) => s.sim > 0).sort((a, b) => b.sim - a.sim).slice(0, 5);
|
|
216
|
+
const patterns = detectPatterns({ repoDir: input.repoDir });
|
|
217
|
+
const recurring = patterns.find((p) => p.subject.toLowerCase() === input.subject.toLowerCase()) ?? null;
|
|
218
|
+
const rationale = [];
|
|
219
|
+
if (scored.length === 0)
|
|
220
|
+
rationale.push("No historical observations match this signature — looks novel.");
|
|
221
|
+
else
|
|
222
|
+
rationale.push(`Found ${scored.length} similar past observation(s); top similarity ${scored[0].sim.toFixed(2)}.`);
|
|
223
|
+
if (recurring)
|
|
224
|
+
rationale.push(`Subject "${recurring.subject}" has recurred ${recurring.count} times${recurring.window ? ` (${recurring.window})` : ""} — this is likely a pattern, not a one-off.`);
|
|
225
|
+
return {
|
|
226
|
+
hypotheses: scored.map((s) => ({
|
|
227
|
+
matchingObservation: {
|
|
228
|
+
id: s.o.id, ts: s.o.ts, host: s.o.host, kind: s.o.kind, detail: s.o.detail,
|
|
229
|
+
},
|
|
230
|
+
similarity: Math.round(s.sim * 1000) / 1000,
|
|
231
|
+
notes: `kind=${s.o.kind} on ${s.o.host}`,
|
|
232
|
+
})),
|
|
233
|
+
recurring,
|
|
234
|
+
rationale,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
/** One-line pulse. */
|
|
238
|
+
export function formatInfraLine(opts = {}) {
|
|
239
|
+
const all = readAll(observationsPath(opts.repoDir));
|
|
240
|
+
const patterns = detectPatterns({ repoDir: opts.repoDir });
|
|
241
|
+
return `INFRA · ${all.length} obs · ${patterns.length} patterns`;
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/infra_brain/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"infra_brain.test.d.ts","sourceRoot":"","sources":["../../src/infra_brain/infra_brain.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { recordObservation, verifyObservation, detectPatterns, exportDigest, verifyDigest, ingestDigest, diagnose, formatInfraLine, } from "./index.js";
|
|
6
|
+
describe("v2.14 · INFRA AS AI — host brain + gossip primitive", () => {
|
|
7
|
+
let dir;
|
|
8
|
+
beforeEach(() => { dir = mkdtempSync(join(tmpdir(), "infra-")); });
|
|
9
|
+
afterEach(() => { try {
|
|
10
|
+
rmSync(dir, { recursive: true, force: true });
|
|
11
|
+
}
|
|
12
|
+
catch { } });
|
|
13
|
+
describe("observations", () => {
|
|
14
|
+
it("recordObservation appends signed entry", () => {
|
|
15
|
+
const o = recordObservation({
|
|
16
|
+
kind: "latency_outlier", subject: "auth-service",
|
|
17
|
+
detail: "p99 spiked to 3.2s",
|
|
18
|
+
metric: { name: "p99_latency_ms", value: 3200, unit: "ms" },
|
|
19
|
+
repoDir: dir,
|
|
20
|
+
});
|
|
21
|
+
expect(o.id).toMatch(/^o-/);
|
|
22
|
+
expect(o.sig).toMatch(/^[0-9a-f]{64}$/);
|
|
23
|
+
expect(o.metric?.value).toBe(3200);
|
|
24
|
+
});
|
|
25
|
+
it("verifyObservation passes for un-tampered", () => {
|
|
26
|
+
const o = recordObservation({ kind: "deploy", subject: "api", detail: "v2.13.1", repoDir: dir });
|
|
27
|
+
expect(verifyObservation(o)).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
it("verifyObservation fails on tamper", () => {
|
|
30
|
+
const o = recordObservation({ kind: "deploy", subject: "api", detail: "v2.13.1", repoDir: dir });
|
|
31
|
+
const tampered = { ...o, detail: "TAMPERED" };
|
|
32
|
+
expect(verifyObservation(tampered)).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe("pattern detection", () => {
|
|
36
|
+
it("detects recurring subject + kind", () => {
|
|
37
|
+
for (let i = 0; i < 5; i++) {
|
|
38
|
+
recordObservation({ kind: "cron_misfire", subject: "nightly-backup", detail: "missed run", repoDir: dir });
|
|
39
|
+
}
|
|
40
|
+
const patterns = detectPatterns({ repoDir: dir });
|
|
41
|
+
const p = patterns.find((x) => x.subject === "nightly-backup");
|
|
42
|
+
expect(p).toBeDefined();
|
|
43
|
+
expect(p.count).toBe(5);
|
|
44
|
+
});
|
|
45
|
+
it("ignores singletons by default", () => {
|
|
46
|
+
recordObservation({ kind: "anomaly", subject: "thing-x", detail: "weird", repoDir: dir });
|
|
47
|
+
const patterns = detectPatterns({ repoDir: dir });
|
|
48
|
+
expect(patterns.find((x) => x.subject === "thing-x")).toBeUndefined();
|
|
49
|
+
});
|
|
50
|
+
it("estimates a window when timestamps cluster", () => {
|
|
51
|
+
// Hand-craft 4 observations all at ~15:00 UTC
|
|
52
|
+
const now = new Date();
|
|
53
|
+
now.setUTCHours(15, 0, 0, 0);
|
|
54
|
+
for (let day = 0; day < 4; day++) {
|
|
55
|
+
const ts = new Date(now.getTime() - day * 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
56
|
+
// Use the file directly to inject a custom timestamp
|
|
57
|
+
const fs = require("node:fs");
|
|
58
|
+
const path = require("node:path").join(dir, ".mneme", "infra", "observations.jsonl");
|
|
59
|
+
// Bootstrap by writing one normal entry first
|
|
60
|
+
if (day === 0)
|
|
61
|
+
recordObservation({ kind: "deploy", subject: "x", detail: "y", repoDir: dir });
|
|
62
|
+
const lines = fs.readFileSync(path, "utf8").trim().split("\n");
|
|
63
|
+
// Append synthetic
|
|
64
|
+
const obs = JSON.parse(lines[0]);
|
|
65
|
+
const synthetic = { ...obs, id: "o-syn" + day, ts, subject: "deploy-3pm" };
|
|
66
|
+
fs.appendFileSync(path, JSON.stringify(synthetic) + "\n");
|
|
67
|
+
}
|
|
68
|
+
const patterns = detectPatterns({ repoDir: dir });
|
|
69
|
+
const p = patterns.find((x) => x.subject === "deploy-3pm");
|
|
70
|
+
expect(p?.window).toBeDefined();
|
|
71
|
+
expect(p.window).toMatch(/15:00 UTC/);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe("gossip / digest", () => {
|
|
75
|
+
it("exportDigest produces signed digest", () => {
|
|
76
|
+
for (let i = 0; i < 3; i++)
|
|
77
|
+
recordObservation({ kind: "error_spike", subject: "api", detail: "5xx burst", repoDir: dir });
|
|
78
|
+
const d = exportDigest({ repoDir: dir });
|
|
79
|
+
expect(d.observationCount).toBe(3);
|
|
80
|
+
expect(d.sig).toMatch(/^[0-9a-f]{64}$/);
|
|
81
|
+
});
|
|
82
|
+
it("verifyDigest passes for un-tampered", () => {
|
|
83
|
+
const d = exportDigest({ repoDir: dir });
|
|
84
|
+
expect(verifyDigest(d)).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
it("verifyDigest fails on tamper", () => {
|
|
87
|
+
const d = exportDigest({ repoDir: dir });
|
|
88
|
+
const tampered = { ...d, host: "FORGED" };
|
|
89
|
+
expect(verifyDigest(tampered)).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
it("ingestDigest learns patterns from a peer", () => {
|
|
92
|
+
// Build digest in dir1
|
|
93
|
+
const dir1 = mkdtempSync(join(tmpdir(), "infra1-"));
|
|
94
|
+
try {
|
|
95
|
+
for (let i = 0; i < 3; i++)
|
|
96
|
+
recordObservation({ kind: "error_spike", subject: "billing-service", detail: "5xx", repoDir: dir1 });
|
|
97
|
+
const d = exportDigest({ repoDir: dir1 });
|
|
98
|
+
// Ingest into our dir (different host)
|
|
99
|
+
const r = ingestDigest(d, { repoDir: dir });
|
|
100
|
+
expect(r.accepted).toBe(true);
|
|
101
|
+
expect(r.patternsLearned).toBeGreaterThan(0);
|
|
102
|
+
// Should have appended observations tagged from:peer
|
|
103
|
+
const patterns = detectPatterns({ repoDir: dir });
|
|
104
|
+
// Pattern detection requires ≥2 occurrences but ingest only adds 1 synthetic per pattern.
|
|
105
|
+
// So we check the underlying observations file directly:
|
|
106
|
+
const fs = require("node:fs");
|
|
107
|
+
const path = require("node:path").join(dir, ".mneme", "infra", "observations.jsonl");
|
|
108
|
+
const content = fs.readFileSync(path, "utf8");
|
|
109
|
+
expect(content).toContain("[peer:");
|
|
110
|
+
expect(content).toContain("billing-service");
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
rmSync(dir1, { recursive: true, force: true });
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
it("ingestDigest rejects unverified peer", () => {
|
|
117
|
+
const d = exportDigest({ repoDir: dir });
|
|
118
|
+
const tampered = { ...d, host: "FORGED" };
|
|
119
|
+
const r = ingestDigest(tampered, { repoDir: dir });
|
|
120
|
+
expect(r.accepted).toBe(false);
|
|
121
|
+
expect(r.reason).toContain("sig mismatch");
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
describe("diagnose", () => {
|
|
125
|
+
it("matches similar past observations", () => {
|
|
126
|
+
recordObservation({ kind: "error_spike", subject: "auth-service", detail: "JWT verification timing out under load", repoDir: dir });
|
|
127
|
+
recordObservation({ kind: "deploy", subject: "auth-service", detail: "rolled back v3.2.1", repoDir: dir });
|
|
128
|
+
const r = diagnose({ subject: "auth-service", detail: "JWT verification slow under load", repoDir: dir });
|
|
129
|
+
expect(r.hypotheses.length).toBeGreaterThan(0);
|
|
130
|
+
expect(r.hypotheses[0].similarity).toBeGreaterThan(0);
|
|
131
|
+
});
|
|
132
|
+
it("flags recurring patterns", () => {
|
|
133
|
+
for (let i = 0; i < 4; i++) {
|
|
134
|
+
recordObservation({ kind: "cron_misfire", subject: "nightly-backup", detail: "missed run", repoDir: dir });
|
|
135
|
+
}
|
|
136
|
+
const r = diagnose({ subject: "nightly-backup", detail: "no backup last night", repoDir: dir });
|
|
137
|
+
expect(r.recurring).not.toBeNull();
|
|
138
|
+
expect(r.recurring.count).toBe(4);
|
|
139
|
+
expect(r.rationale.some((s) => s.includes("recurred"))).toBe(true);
|
|
140
|
+
});
|
|
141
|
+
it("novel symptom rationale is honest", () => {
|
|
142
|
+
const r = diagnose({ subject: "totally-new-thing", detail: "never seen", repoDir: dir });
|
|
143
|
+
expect(r.hypotheses).toHaveLength(0);
|
|
144
|
+
expect(r.rationale[0]).toContain("novel");
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
it("formatInfraLine summarises", () => {
|
|
148
|
+
recordObservation({ kind: "deploy", subject: "x", detail: "y", repoDir: dir });
|
|
149
|
+
const line = formatInfraLine({ repoDir: dir });
|
|
150
|
+
expect(line).toContain("INFRA");
|
|
151
|
+
expect(line).toContain("1 obs");
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
//# sourceMappingURL=infra_brain.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"infra_brain.test.js","sourceRoot":"","sources":["../../src/infra_brain/infra_brain.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.14.0 — KILL SWITCH PROTOCOL
|
|
3
|
+
*
|
|
4
|
+
* "Three things every CISO loses sleep over:
|
|
5
|
+
* 1. AI hallucinates → costly decision → lawsuit
|
|
6
|
+
* 2. AI vendor changes pricing/TOS → migration nightmare
|
|
7
|
+
* 3. AI exfiltrates secrets/PII → data breach
|
|
8
|
+
* Mneme answers all three with one bundle."
|
|
9
|
+
*
|
|
10
|
+
* Three layered defenses, each HMAC-signed for court-admissible audit:
|
|
11
|
+
*
|
|
12
|
+
* 1. KILL SWITCH — admin-issued signed directive that, when broadcast,
|
|
13
|
+
* tells every Mneme-aware AI agent to refuse to respond. Stamped
|
|
14
|
+
* with HMAC; receivers verify before honouring (so attackers can't
|
|
15
|
+
* forge a kill).
|
|
16
|
+
*
|
|
17
|
+
* 2. AUDIT LOG — every AI interaction is recorded with HMAC chain
|
|
18
|
+
* (similar to BOUNTY's structure but broader scope: prompts,
|
|
19
|
+
* responses, file accesses, tool calls). Tamper-evident.
|
|
20
|
+
*
|
|
21
|
+
* 3. DLP (Data Leakage Prevention) — scan outbound AI prompts /
|
|
22
|
+
* responses for secrets / PII patterns BEFORE they leave the org.
|
|
23
|
+
* Block + log violations.
|
|
24
|
+
*
|
|
25
|
+
* Storage: `.mneme/compliance/` directory:
|
|
26
|
+
* - kill_switch.json — current state of the kill directive
|
|
27
|
+
* - audit.jsonl — append-only audit chain
|
|
28
|
+
* - dlp_rules.json — current rule set (org can override defaults)
|
|
29
|
+
*
|
|
30
|
+
* Composes orthogonally with existing `aegis/` (immune system) and
|
|
31
|
+
* `ai_compliance.ts` (compliance metrics) — those measure; KILL SWITCH
|
|
32
|
+
* acts.
|
|
33
|
+
*/
|
|
34
|
+
declare const PROTOCOL_VERSION: 1;
|
|
35
|
+
export interface KillSwitchDirective {
|
|
36
|
+
v: typeof PROTOCOL_VERSION;
|
|
37
|
+
/** "active" = refuse all AI. "scoped" = refuse for specific tags. "off" = normal. */
|
|
38
|
+
state: "active" | "scoped" | "off";
|
|
39
|
+
/** Free-form reason for the kill. */
|
|
40
|
+
reason: string;
|
|
41
|
+
/** Issuer identity (e.g., "ciso@company.com"). */
|
|
42
|
+
issuedBy: string;
|
|
43
|
+
issuedAt: string;
|
|
44
|
+
/** Optional scopes — when state="scoped", only these vendors / tags are killed. */
|
|
45
|
+
scopes?: {
|
|
46
|
+
vendors?: string[];
|
|
47
|
+
tags?: string[];
|
|
48
|
+
};
|
|
49
|
+
/** Optional expiry — auto-clears at this time. */
|
|
50
|
+
expiresAt?: string;
|
|
51
|
+
/** HMAC over the directive body. */
|
|
52
|
+
sig: string;
|
|
53
|
+
}
|
|
54
|
+
export interface IssueKillInput {
|
|
55
|
+
state: "active" | "scoped" | "off";
|
|
56
|
+
reason: string;
|
|
57
|
+
issuedBy: string;
|
|
58
|
+
scopes?: KillSwitchDirective["scopes"];
|
|
59
|
+
expiresAt?: string;
|
|
60
|
+
repoDir?: string;
|
|
61
|
+
secret?: string;
|
|
62
|
+
}
|
|
63
|
+
export declare function issueKillSwitch(input: IssueKillInput): KillSwitchDirective;
|
|
64
|
+
export declare function readKillSwitch(opts?: {
|
|
65
|
+
repoDir?: string;
|
|
66
|
+
}): KillSwitchDirective | null;
|
|
67
|
+
export declare function verifyKillSwitch(d: KillSwitchDirective, secret?: string): {
|
|
68
|
+
ok: boolean;
|
|
69
|
+
reason?: string;
|
|
70
|
+
};
|
|
71
|
+
export interface ShouldRespondInput {
|
|
72
|
+
vendor?: string;
|
|
73
|
+
tags?: string[];
|
|
74
|
+
repoDir?: string;
|
|
75
|
+
secret?: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The runtime check. Mneme-aware AI clients call this before every
|
|
79
|
+
* response. If kill switch is active (or scoped to them), they MUST
|
|
80
|
+
* refuse. Returns an instruction to embed in the refusal.
|
|
81
|
+
*/
|
|
82
|
+
export declare function shouldRespond(input?: ShouldRespondInput): {
|
|
83
|
+
allowed: boolean;
|
|
84
|
+
reason?: string;
|
|
85
|
+
killDirective?: KillSwitchDirective;
|
|
86
|
+
};
|
|
87
|
+
export interface AuditEntry {
|
|
88
|
+
v: typeof PROTOCOL_VERSION;
|
|
89
|
+
id: string;
|
|
90
|
+
ts: string;
|
|
91
|
+
kind: "prompt" | "response" | "tool_call" | "file_read" | "kill_switch" | "dlp_block" | "other";
|
|
92
|
+
actor: string;
|
|
93
|
+
detail: string;
|
|
94
|
+
/** Optional metadata (vendor, tool name, hash of content, etc). */
|
|
95
|
+
meta?: Record<string, string | number | boolean>;
|
|
96
|
+
/** chainSig = HMAC(prev.chainSig + canonical(this)). */
|
|
97
|
+
chainSig: string;
|
|
98
|
+
}
|
|
99
|
+
export interface RecordAuditInput {
|
|
100
|
+
kind: AuditEntry["kind"];
|
|
101
|
+
actor: string;
|
|
102
|
+
detail: string;
|
|
103
|
+
meta?: AuditEntry["meta"];
|
|
104
|
+
repoDir?: string;
|
|
105
|
+
secret?: string;
|
|
106
|
+
}
|
|
107
|
+
export declare function recordAudit(input: RecordAuditInput): AuditEntry;
|
|
108
|
+
export declare function verifyAuditChain(opts?: {
|
|
109
|
+
repoDir?: string;
|
|
110
|
+
secret?: string;
|
|
111
|
+
}): {
|
|
112
|
+
ok: boolean;
|
|
113
|
+
total: number;
|
|
114
|
+
brokenIndex: number;
|
|
115
|
+
brokenReason?: string;
|
|
116
|
+
};
|
|
117
|
+
/** Export the audit log for compliance reporting (CISO weekly etc). */
|
|
118
|
+
export declare function exportAuditReport(opts?: {
|
|
119
|
+
repoDir?: string;
|
|
120
|
+
since?: string;
|
|
121
|
+
}): {
|
|
122
|
+
entries: AuditEntry[];
|
|
123
|
+
total: number;
|
|
124
|
+
byKind: Record<string, number>;
|
|
125
|
+
byActor: Record<string, number>;
|
|
126
|
+
generatedAt: string;
|
|
127
|
+
chainOk: boolean;
|
|
128
|
+
};
|
|
129
|
+
export interface DlpRule {
|
|
130
|
+
id: string;
|
|
131
|
+
pattern: string;
|
|
132
|
+
flags?: string;
|
|
133
|
+
severity: "warn" | "block";
|
|
134
|
+
category: "secret" | "pii" | "credential" | "internal" | "other";
|
|
135
|
+
description: string;
|
|
136
|
+
}
|
|
137
|
+
export interface DlpScanResult {
|
|
138
|
+
hits: Array<{
|
|
139
|
+
ruleId: string;
|
|
140
|
+
category: DlpRule["category"];
|
|
141
|
+
severity: "warn" | "block";
|
|
142
|
+
match: string;
|
|
143
|
+
description: string;
|
|
144
|
+
}>;
|
|
145
|
+
worstSeverity: "none" | "warn" | "block";
|
|
146
|
+
blocked: boolean;
|
|
147
|
+
scanned: number;
|
|
148
|
+
signedAt: string;
|
|
149
|
+
sig: string;
|
|
150
|
+
}
|
|
151
|
+
export declare function loadDlpRules(opts?: {
|
|
152
|
+
repoDir?: string;
|
|
153
|
+
}): DlpRule[];
|
|
154
|
+
/**
|
|
155
|
+
* Scan a string for DLP violations. Returns hits + verdict. Calls
|
|
156
|
+
* recordAudit("dlp_block") when blocking — court-admissible trace.
|
|
157
|
+
*/
|
|
158
|
+
export declare function dlpScan(text: string, opts?: {
|
|
159
|
+
repoDir?: string;
|
|
160
|
+
actor?: string;
|
|
161
|
+
secret?: string;
|
|
162
|
+
}): DlpScanResult;
|
|
163
|
+
/** One-line pulse summary. */
|
|
164
|
+
export declare function formatCompliancePulse(opts?: {
|
|
165
|
+
repoDir?: string;
|
|
166
|
+
}): string;
|
|
167
|
+
export {};
|
|
168
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/kill_switch/index.ts"],"names":[],"mappings":"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"}
|