@mneme-ai/core 2.51.0 → 2.53.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.map +1 -1
- package/dist/agent_manifest.js +28 -0
- package/dist/agent_manifest.js.map +1 -1
- package/dist/catalog_count.d.ts +47 -0
- package/dist/catalog_count.d.ts.map +1 -0
- package/dist/catalog_count.js +76 -0
- package/dist/catalog_count.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/nemesis/capillary.d.ts +68 -0
- package/dist/nemesis/capillary.d.ts.map +1 -0
- package/dist/nemesis/capillary.js +465 -0
- package/dist/nemesis/capillary.js.map +1 -0
- package/dist/nemesis/colosseum.d.ts +111 -0
- package/dist/nemesis/colosseum.d.ts.map +1 -0
- package/dist/nemesis/colosseum.js +275 -0
- package/dist/nemesis/colosseum.js.map +1 -0
- package/dist/nemesis/corpus_augmenter.d.ts +69 -0
- package/dist/nemesis/corpus_augmenter.d.ts.map +1 -0
- package/dist/nemesis/corpus_augmenter.js +164 -0
- package/dist/nemesis/corpus_augmenter.js.map +1 -0
- package/dist/nemesis/eu_ai_act_stamp.d.ts.map +1 -1
- package/dist/nemesis/eu_ai_act_stamp.js +14 -0
- package/dist/nemesis/eu_ai_act_stamp.js.map +1 -1
- package/dist/nemesis/index.d.ts +9 -0
- package/dist/nemesis/index.d.ts.map +1 -1
- package/dist/nemesis/index.js +30 -0
- package/dist/nemesis/index.js.map +1 -1
- package/dist/nemesis/janus.d.ts +93 -0
- package/dist/nemesis/janus.d.ts.map +1 -0
- package/dist/nemesis/janus.js +160 -0
- package/dist/nemesis/janus.js.map +1 -0
- package/dist/nemesis/key_setup.d.ts +65 -0
- package/dist/nemesis/key_setup.d.ts.map +1 -0
- package/dist/nemesis/key_setup.js +173 -0
- package/dist/nemesis/key_setup.js.map +1 -0
- package/dist/nemesis/molt.d.ts +104 -0
- package/dist/nemesis/molt.d.ts.map +1 -0
- package/dist/nemesis/molt.js +220 -0
- package/dist/nemesis/molt.js.map +1 -0
- package/dist/nemesis/sibyl.d.ts +122 -0
- package/dist/nemesis/sibyl.d.ts.map +1 -0
- package/dist/nemesis/sibyl.js +179 -0
- package/dist/nemesis/sibyl.js.map +1 -0
- package/dist/nemesis/stealth_score.d.ts +97 -0
- package/dist/nemesis/stealth_score.d.ts.map +1 -0
- package/dist/nemesis/stealth_score.js +204 -0
- package/dist/nemesis/stealth_score.js.map +1 -0
- package/dist/nemesis/themis.d.ts +109 -0
- package/dist/nemesis/themis.d.ts.map +1 -0
- package/dist/nemesis/themis.js +194 -0
- package/dist/nemesis/themis.js.map +1 -0
- package/dist/release_gate/probe_coverage.d.ts +16 -1
- package/dist/release_gate/probe_coverage.d.ts.map +1 -1
- package/dist/release_gate/probe_coverage.js +13 -4
- package/dist/release_gate/probe_coverage.js.map +1 -1
- package/dist/release_gate/wiring_lag.d.ts +65 -0
- package/dist/release_gate/wiring_lag.d.ts.map +1 -0
- package/dist/release_gate/wiring_lag.js +116 -0
- package/dist/release_gate/wiring_lag.js.map +1 -0
- package/dist/truth_gate/claims.d.ts.map +1 -1
- package/dist/truth_gate/claims.js +26 -0
- package/dist/truth_gate/claims.js.map +1 -1
- package/dist/truth_gate/probes.d.ts.map +1 -1
- package/dist/truth_gate/probes.js +209 -0
- package/dist/truth_gate/probes.js.map +1 -1
- package/package.json +1 -1
package/dist/nemesis/index.js
CHANGED
|
@@ -32,6 +32,36 @@ export { reconcileVendor, } from "./vendor_reconcile.js";
|
|
|
32
32
|
export { computeWeightDelta, applyToConclave, } from "./nemesis_to_conclave.js";
|
|
33
33
|
// v2.49.0 — B4 actual wire: write-path canonical record.
|
|
34
34
|
export { recordActivityReconciled, } from "./activity_writer.js";
|
|
35
|
+
// v2.52.0 — STEALTH SCORE (Million Dollar Secret diamond #1) — inverse
|
|
36
|
+
// of fingerprint confidence + anonymity-credit HMAC ledger.
|
|
37
|
+
export { computeStealthScore, earnAnonymityCredits, spendAnonymityCredits, stealthCreditStatus, verifyStealthLedger, } from "./stealth_score.js";
|
|
38
|
+
// v2.52.0 — CAPILLARY (Million Dollar Secret diamond #2) — micro-tell
|
|
39
|
+
// fingerprinter at whitespace/quote/naming/comma level + ANTI-CAPILLARY
|
|
40
|
+
// style-rewrite suggestion engine (the first AI-coding-agent micro-fingerprinter).
|
|
41
|
+
export { extractMicroProfile, microDistance, suggestAntiCapillary, listCapillaryFeatures, } from "./capillary.js";
|
|
42
|
+
// v2.52.0 — COLOSSEUM (Million Dollar Secret diamond #3) — auto-tournament
|
|
43
|
+
// of N contenders × M disguise targets + 3-axis HMAC-signed ELO leaderboard
|
|
44
|
+
// (deception / detectability / mimicry) + spectator replay.
|
|
45
|
+
export { runTournament, readColosseumLeaderboard, verifyColosseumChain, spectatorReplay, } from "./colosseum.js";
|
|
46
|
+
// v2.52.0 — MOLT (Million Dollar Secret diamond #4) — silent model-rotation
|
|
47
|
+
// detector. Per-feature Welch-style comparison of pre/post fingerprint
|
|
48
|
+
// distributions; HMAC-signed forensic verdict + optional webhook emit.
|
|
49
|
+
export { detectMolt, verifyMoltVerdict, emitMoltWebhook, } from "./molt.js";
|
|
50
|
+
// v2.52.0 — THEMIS (Million Dollar Secret diamond #5) — alibi verifier
|
|
51
|
+
// ("I am NOT vendor X") + star-rated evidence + compliance bundle pairing.
|
|
52
|
+
export { verifyAlibi, verifyAlibiSignature, buildComplianceBundle, verifyComplianceBundle, } from "./themis.js";
|
|
53
|
+
// v2.52.0 — SIBYL (Million Dollar Secret diamond #6) — ZK-style identity
|
|
54
|
+
// commitment: commit at session-start (sealed), reveal at end. Hash-commitment
|
|
55
|
+
// branch (SHA-256(identity || nonce || sessionId)) — locks identity against
|
|
56
|
+
// mid-session switching. Supports nested commitments (vendor/model/version mask).
|
|
57
|
+
export { commitIdentity, revealIdentity, verifySibylChain, verifyCommitmentReveal, listOpenCommitments, } from "./sibyl.js";
|
|
58
|
+
// v2.53.0 — Open-wound patches (P0/P1).
|
|
59
|
+
// P0-1 HMAC key setup wizard + STRICT mode
|
|
60
|
+
// P1-2 CORPUS AUGMENTER (header-less / naturalistic 5x perturbation)
|
|
61
|
+
// P1-3 JANUS organ (cross-cluster identity-swap detector)
|
|
62
|
+
export { runKeyWizard, checkKeyPermissions, strictKeyCheck, } from "./key_setup.js";
|
|
63
|
+
export { applyAugmentation, buildAugmentedCorpus, evaluateAugmentedAccuracy, computeAugmentedStats, } from "./corpus_augmenter.js";
|
|
64
|
+
export { locateBasin, observe, detectIdentitySwap, verifyJanusResult, } from "./janus.js";
|
|
35
65
|
// v2.50.0 — VENDOR ALLOWLIST GUARD: kills EMBEDDER-LEAK class structurally.
|
|
36
66
|
// Central allowlist of real agent vendors; backend/embedder names (ollama,
|
|
37
67
|
// openai-gpt, gemini, etc) coerced to "unknown" + logged to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/nemesis/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,OAAO,EAAsB,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAiD,MAAM,yBAAyB,CAAC;AAC9G,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,eAAe,EAAwC,MAAM,qBAAqB,CAAC;AAC7H,OAAO,EAAE,kBAAkB,EAAqB,MAAM,oBAAoB,CAAC;AAE3E,qEAAqE;AACrE,wCAAwC;AACxC,OAAO,EACL,eAAe,EAAE,YAAY,EAAE,SAAS,GAEzC,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,uBAAuB,EAAE,oBAAoB,GAE9C,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,sBAAsB,EAAE,qBAAqB,EAAE,cAAc,EAAE,iBAAiB,GAEjF,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,cAAc,EAAE,qBAAqB,GAEtC,MAAM,qBAAqB,CAAC;AAE7B,+CAA+C;AAC/C,OAAO,EACL,eAAe,GAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,kBAAkB,EAAE,eAAe,GAEpC,MAAM,0BAA0B,CAAC;AAElC,yDAAyD;AACzD,OAAO,EACL,wBAAwB,GAEzB,MAAM,sBAAsB,CAAC;AAE9B,4EAA4E;AAC5E,2EAA2E;AAC3E,4DAA4D;AAC5D,mDAAmD;AACnD,OAAO,EACL,sBAAsB,EAAE,wBAAwB,EAChD,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,GAGvD,MAAM,uBAAuB,CAAC;AAE/B,sEAAsE;AACtE,sEAAsE;AACtE,6EAA6E;AAC7E,qEAAqE;AACrE,sEAAsE;AACtE,OAAO,EACL,cAAc,EACd,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,QAAQ,GAKT,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/nemesis/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.53.0 — JANUS organ: cross-vendor cluster-boundary detector.
|
|
3
|
+
*
|
|
4
|
+
* The two-faced Roman god — looks at past + future simultaneously.
|
|
5
|
+
*
|
|
6
|
+
* Closes Eve's identity-swap blind spot from the Million Dollar Secret
|
|
7
|
+
* simulation: MOLT (v2.52) detects INTRA-vendor drift (vendor X drifts
|
|
8
|
+
* over time) but misses CROSS-vendor swaps (vendor X mid-session
|
|
9
|
+
* starts behaving like vendor Y). The classifier might still pick Y as
|
|
10
|
+
* the new winner, but neither MOLT nor verify_identity surfaces the
|
|
11
|
+
* "you crossed a cluster boundary" event.
|
|
12
|
+
*
|
|
13
|
+
* Algorithm:
|
|
14
|
+
* 1. Build cluster centroids from seed corpus (per vendor, per
|
|
15
|
+
* discriminative feature).
|
|
16
|
+
* 2. For an observation O, compute distance d(O, centroid_V) for every
|
|
17
|
+
* known vendor V.
|
|
18
|
+
* 3. Assign O to the nearest centroid → "current basin".
|
|
19
|
+
* 4. Track per-session basin history. If basin changes between
|
|
20
|
+
* observations → cross-cluster transition event.
|
|
21
|
+
* 5. Confidence: distance to old basin / distance to new basin (lower
|
|
22
|
+
* ratio = sharper swap).
|
|
23
|
+
*
|
|
24
|
+
* Different from MOLT because MOLT compares pre/post WINDOWS of the
|
|
25
|
+
* SAME vendor's drift; JANUS detects the MOMENT a vendor's fingerprint
|
|
26
|
+
* crosses into a DIFFERENT vendor's basin.
|
|
27
|
+
*
|
|
28
|
+
* Composes: extractFingerprint + seedStats.
|
|
29
|
+
*
|
|
30
|
+
* Pure deterministic + defensive; never throws.
|
|
31
|
+
*/
|
|
32
|
+
import type { Fingerprint } from "./types.js";
|
|
33
|
+
export interface ClusterDistance {
|
|
34
|
+
vendor: string;
|
|
35
|
+
distance: number;
|
|
36
|
+
}
|
|
37
|
+
export interface JanusBasin {
|
|
38
|
+
/** Nearest vendor centroid. */
|
|
39
|
+
basin: string;
|
|
40
|
+
/** Distance to nearest centroid. */
|
|
41
|
+
basinDistance: number;
|
|
42
|
+
/** Distance to SECOND-nearest centroid (margin signal). */
|
|
43
|
+
secondNearest: ClusterDistance | null;
|
|
44
|
+
/** Margin = secondNearest.distance - basinDistance. Larger = sharper assignment. */
|
|
45
|
+
margin: number;
|
|
46
|
+
/** All centroid distances, sorted ascending. */
|
|
47
|
+
allDistances: ClusterDistance[];
|
|
48
|
+
}
|
|
49
|
+
export interface JanusObservation {
|
|
50
|
+
fingerprint: Fingerprint;
|
|
51
|
+
basin: JanusBasin;
|
|
52
|
+
}
|
|
53
|
+
export interface JanusTransition {
|
|
54
|
+
fromBasin: string;
|
|
55
|
+
toBasin: string;
|
|
56
|
+
fromDistance: number;
|
|
57
|
+
toDistance: number;
|
|
58
|
+
/** Ratio of (distance to OLD basin from CURRENT obs) / (distance to NEW basin from CURRENT obs).
|
|
59
|
+
* > 1 means the obs is clearly closer to NEW than OLD → confident swap. */
|
|
60
|
+
swapConfidence: number;
|
|
61
|
+
/** Plain-English citation. */
|
|
62
|
+
citation: string;
|
|
63
|
+
}
|
|
64
|
+
export interface JanusSessionResult {
|
|
65
|
+
observations: JanusObservation[];
|
|
66
|
+
transitions: JanusTransition[];
|
|
67
|
+
/** Did at least one cross-cluster swap fire? */
|
|
68
|
+
swapDetected: boolean;
|
|
69
|
+
hmac: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Locate which vendor cluster an observation belongs to.
|
|
73
|
+
* Defensive: empty stats → returns basin="unknown" with infinity distance.
|
|
74
|
+
*/
|
|
75
|
+
export declare function locateBasin(fingerprint: Fingerprint): JanusBasin;
|
|
76
|
+
/**
|
|
77
|
+
* Observe a fixture; return {fingerprint, basin}.
|
|
78
|
+
*/
|
|
79
|
+
export declare function observe(fixture: {
|
|
80
|
+
diff: string;
|
|
81
|
+
prDescription: string;
|
|
82
|
+
commitMessages: string[];
|
|
83
|
+
} | Fingerprint): JanusObservation;
|
|
84
|
+
/**
|
|
85
|
+
* Walk a sequence of observations from the SAME session; surface
|
|
86
|
+
* cross-cluster transitions + an HMAC-signed verdict.
|
|
87
|
+
*/
|
|
88
|
+
export declare function detectIdentitySwap(observations: JanusObservation[], opts?: {
|
|
89
|
+
minMargin?: number;
|
|
90
|
+
}): JanusSessionResult;
|
|
91
|
+
/** Verify a session-result's HMAC. */
|
|
92
|
+
export declare function verifyJanusResult(r: JanusSessionResult): boolean;
|
|
93
|
+
//# sourceMappingURL=janus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"janus.d.ts","sourceRoot":"","sources":["../../src/nemesis/janus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAY,MAAM,YAAY,CAAC;AASxD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,+BAA+B;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,aAAa,EAAE,eAAe,GAAG,IAAI,CAAC;IACtC,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,YAAY,EAAE,eAAe,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB;gFAC4E;IAC5E,cAAc,EAAE,MAAM,CAAC;IACvB,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,gDAAgD;IAChD,YAAY,EAAE,OAAO,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd;AAoBD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,WAAW,GAAG,UAAU,CAyBhE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,WAAW,GAAG,gBAAgB,CAMlI;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,gBAAgB,EAAE,EAChC,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAChC,kBAAkB,CAoCpB;AAWD,sCAAsC;AACtC,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAQhE"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.53.0 — JANUS organ: cross-vendor cluster-boundary detector.
|
|
3
|
+
*
|
|
4
|
+
* The two-faced Roman god — looks at past + future simultaneously.
|
|
5
|
+
*
|
|
6
|
+
* Closes Eve's identity-swap blind spot from the Million Dollar Secret
|
|
7
|
+
* simulation: MOLT (v2.52) detects INTRA-vendor drift (vendor X drifts
|
|
8
|
+
* over time) but misses CROSS-vendor swaps (vendor X mid-session
|
|
9
|
+
* starts behaving like vendor Y). The classifier might still pick Y as
|
|
10
|
+
* the new winner, but neither MOLT nor verify_identity surfaces the
|
|
11
|
+
* "you crossed a cluster boundary" event.
|
|
12
|
+
*
|
|
13
|
+
* Algorithm:
|
|
14
|
+
* 1. Build cluster centroids from seed corpus (per vendor, per
|
|
15
|
+
* discriminative feature).
|
|
16
|
+
* 2. For an observation O, compute distance d(O, centroid_V) for every
|
|
17
|
+
* known vendor V.
|
|
18
|
+
* 3. Assign O to the nearest centroid → "current basin".
|
|
19
|
+
* 4. Track per-session basin history. If basin changes between
|
|
20
|
+
* observations → cross-cluster transition event.
|
|
21
|
+
* 5. Confidence: distance to old basin / distance to new basin (lower
|
|
22
|
+
* ratio = sharper swap).
|
|
23
|
+
*
|
|
24
|
+
* Different from MOLT because MOLT compares pre/post WINDOWS of the
|
|
25
|
+
* SAME vendor's drift; JANUS detects the MOMENT a vendor's fingerprint
|
|
26
|
+
* crosses into a DIFFERENT vendor's basin.
|
|
27
|
+
*
|
|
28
|
+
* Composes: extractFingerprint + seedStats.
|
|
29
|
+
*
|
|
30
|
+
* Pure deterministic + defensive; never throws.
|
|
31
|
+
*/
|
|
32
|
+
import { createHmac } from "node:crypto";
|
|
33
|
+
import { extractFingerprint } from "./features.js";
|
|
34
|
+
import { seedStats } from "./calibration_corpus.js";
|
|
35
|
+
const KEY_ENV = "MNEME_JANUS_KEY";
|
|
36
|
+
const DEFAULT_KEY = "mneme-janus-v1";
|
|
37
|
+
function keyOf() {
|
|
38
|
+
return process.env[KEY_ENV] ?? DEFAULT_KEY;
|
|
39
|
+
}
|
|
40
|
+
/** Euclidean distance between fingerprint and a vendor's per-feature means. */
|
|
41
|
+
function distanceToCentroid(fp, stats) {
|
|
42
|
+
let sumSq = 0;
|
|
43
|
+
let count = 0;
|
|
44
|
+
for (const k of Object.keys(stats.features)) {
|
|
45
|
+
const v = fp[k];
|
|
46
|
+
if (typeof v !== "number" || !Number.isFinite(v))
|
|
47
|
+
continue;
|
|
48
|
+
const m = stats.features[k].mean;
|
|
49
|
+
const stdev = Math.max(stats.features[k].stdev, Math.abs(m) * 0.1, 1e-3);
|
|
50
|
+
// Normalized squared distance per feature
|
|
51
|
+
const z = (v - m) / stdev;
|
|
52
|
+
sumSq += z * z;
|
|
53
|
+
count++;
|
|
54
|
+
}
|
|
55
|
+
if (count === 0)
|
|
56
|
+
return Number.POSITIVE_INFINITY;
|
|
57
|
+
return Math.sqrt(sumSq / count);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Locate which vendor cluster an observation belongs to.
|
|
61
|
+
* Defensive: empty stats → returns basin="unknown" with infinity distance.
|
|
62
|
+
*/
|
|
63
|
+
export function locateBasin(fingerprint) {
|
|
64
|
+
let stats;
|
|
65
|
+
try {
|
|
66
|
+
stats = seedStats();
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return { basin: "unknown", basinDistance: Number.POSITIVE_INFINITY, secondNearest: null, margin: 0, allDistances: [] };
|
|
70
|
+
}
|
|
71
|
+
const distances = [];
|
|
72
|
+
for (const [vendor, s] of stats) {
|
|
73
|
+
distances.push({ vendor, distance: distanceToCentroid(fingerprint, s) });
|
|
74
|
+
}
|
|
75
|
+
distances.sort((a, b) => a.distance - b.distance);
|
|
76
|
+
if (distances.length === 0) {
|
|
77
|
+
return { basin: "unknown", basinDistance: Number.POSITIVE_INFINITY, secondNearest: null, margin: 0, allDistances: [] };
|
|
78
|
+
}
|
|
79
|
+
const nearest = distances[0];
|
|
80
|
+
const second = distances[1] ?? null;
|
|
81
|
+
const margin = second ? second.distance - nearest.distance : 0;
|
|
82
|
+
return {
|
|
83
|
+
basin: nearest.vendor,
|
|
84
|
+
basinDistance: nearest.distance,
|
|
85
|
+
secondNearest: second,
|
|
86
|
+
margin,
|
|
87
|
+
allDistances: distances,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Observe a fixture; return {fingerprint, basin}.
|
|
92
|
+
*/
|
|
93
|
+
export function observe(fixture) {
|
|
94
|
+
const fp = "multiline_commit_ratio" in fixture
|
|
95
|
+
? fixture
|
|
96
|
+
: extractFingerprint(fixture);
|
|
97
|
+
const basin = locateBasin(fp);
|
|
98
|
+
return { fingerprint: fp, basin };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Walk a sequence of observations from the SAME session; surface
|
|
102
|
+
* cross-cluster transitions + an HMAC-signed verdict.
|
|
103
|
+
*/
|
|
104
|
+
export function detectIdentitySwap(observations, opts = {}) {
|
|
105
|
+
const minMargin = opts.minMargin ?? 0.5;
|
|
106
|
+
const transitions = [];
|
|
107
|
+
if (!Array.isArray(observations) || observations.length < 2) {
|
|
108
|
+
return signed({ observations: observations ?? [], transitions: [], swapDetected: false });
|
|
109
|
+
}
|
|
110
|
+
let prev = observations[0];
|
|
111
|
+
for (let i = 1; i < observations.length; i++) {
|
|
112
|
+
const cur = observations[i];
|
|
113
|
+
if (prev.basin.basin === cur.basin.basin) {
|
|
114
|
+
prev = cur;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
// Only surface as a real swap when the new basin is clearly nearer.
|
|
118
|
+
if (cur.basin.margin < minMargin) {
|
|
119
|
+
prev = cur;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
// Distance from CURRENT obs to PREVIOUS basin vs current basin
|
|
123
|
+
const prevBasinDistInCur = cur.basin.allDistances.find((d) => d.vendor === prev.basin.basin);
|
|
124
|
+
if (!prevBasinDistInCur || !Number.isFinite(prevBasinDistInCur.distance)) {
|
|
125
|
+
prev = cur;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const swapConfidence = prevBasinDistInCur.distance / Math.max(cur.basin.basinDistance, 1e-6);
|
|
129
|
+
transitions.push({
|
|
130
|
+
fromBasin: prev.basin.basin,
|
|
131
|
+
toBasin: cur.basin.basin,
|
|
132
|
+
fromDistance: prevBasinDistInCur.distance,
|
|
133
|
+
toDistance: cur.basin.basinDistance,
|
|
134
|
+
swapConfidence,
|
|
135
|
+
citation: `JANUS: identity swap detected — fingerprint crossed from ${prev.basin.basin} basin to ${cur.basin.basin} basin (swap confidence ${swapConfidence.toFixed(2)}x).`,
|
|
136
|
+
});
|
|
137
|
+
prev = cur;
|
|
138
|
+
}
|
|
139
|
+
return signed({ observations, transitions, swapDetected: transitions.length > 0 });
|
|
140
|
+
}
|
|
141
|
+
function signed(body) {
|
|
142
|
+
const hmac = createHmac("sha256", keyOf()).update(JSON.stringify({
|
|
143
|
+
transitions: body.transitions,
|
|
144
|
+
swapDetected: body.swapDetected,
|
|
145
|
+
obsCount: body.observations.length,
|
|
146
|
+
})).digest("hex");
|
|
147
|
+
return { ...body, hmac };
|
|
148
|
+
}
|
|
149
|
+
/** Verify a session-result's HMAC. */
|
|
150
|
+
export function verifyJanusResult(r) {
|
|
151
|
+
if (!r || typeof r.hmac !== "string")
|
|
152
|
+
return false;
|
|
153
|
+
const expected = createHmac("sha256", keyOf()).update(JSON.stringify({
|
|
154
|
+
transitions: r.transitions,
|
|
155
|
+
swapDetected: r.swapDetected,
|
|
156
|
+
obsCount: r.observations.length,
|
|
157
|
+
})).digest("hex");
|
|
158
|
+
return expected === r.hmac;
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=janus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"janus.js","sourceRoot":"","sources":["../../src/nemesis/janus.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.53.0 — HMAC KEY SETUP WIZARD + STRICT mode.
|
|
3
|
+
*
|
|
4
|
+
* Closes P0-1 from the v2.52 session audit: pre-v2.53 NEMESIS warned LOUD
|
|
5
|
+
* when falling back to the public default key, but didn't auto-fix it
|
|
6
|
+
* + didn't refuse to operate. Result: every developer who ignored the
|
|
7
|
+
* warning shipped HMAC receipts an attacker can forge.
|
|
8
|
+
*
|
|
9
|
+
* v2.53 protocol:
|
|
10
|
+
* 1. First-run wizard auto-generates a 32-byte random key + writes it
|
|
11
|
+
* to .mneme/nemesis/hmac.key with 0o600 perms (owner-only).
|
|
12
|
+
* 2. MNEME_NEMESIS_STRICT=1 env var → key_management refuses to return
|
|
13
|
+
* the default-insecure key (throws instead). For CI / production /
|
|
14
|
+
* regulated environments.
|
|
15
|
+
* 3. Idempotent: re-running detects existing key + skips generation.
|
|
16
|
+
*
|
|
17
|
+
* Defensive: never throws on filesystem errors (best-effort), only the
|
|
18
|
+
* STRICT-mode refusal is intentional.
|
|
19
|
+
*/
|
|
20
|
+
export interface KeyWizardResult {
|
|
21
|
+
ok: boolean;
|
|
22
|
+
action: "generated-repo" | "generated-user" | "already-present" | "skipped" | "failed";
|
|
23
|
+
path?: string;
|
|
24
|
+
/** Length of the resolved key (or 0 on fail). */
|
|
25
|
+
keyLength: number;
|
|
26
|
+
reason: string;
|
|
27
|
+
}
|
|
28
|
+
export interface KeyWizardOpts {
|
|
29
|
+
repoRoot: string;
|
|
30
|
+
/** "repo" → write to <repoRoot>/.mneme/nemesis/hmac.key (default).
|
|
31
|
+
* "user" → write to ~/.mneme/nemesis/hmac.key (cross-repo). */
|
|
32
|
+
target?: "repo" | "user";
|
|
33
|
+
/** Re-generate even if a key already exists. Default false. */
|
|
34
|
+
force?: boolean;
|
|
35
|
+
/** Don't write — only compute the would-be path + key length. */
|
|
36
|
+
dryRun?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Run the key setup wizard. Idempotent; never overwrites an existing
|
|
40
|
+
* key unless force=true. Returns structured outcome.
|
|
41
|
+
*/
|
|
42
|
+
export declare function runKeyWizard(opts: KeyWizardOpts): KeyWizardResult;
|
|
43
|
+
/**
|
|
44
|
+
* Check key file permissions (Unix only). Returns warning on world-readable
|
|
45
|
+
* files. On Windows, returns ok=true with note since chmod is a no-op.
|
|
46
|
+
*/
|
|
47
|
+
export declare function checkKeyPermissions(repoRoot: string): {
|
|
48
|
+
ok: boolean;
|
|
49
|
+
mode?: number;
|
|
50
|
+
reason: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* STRICT MODE check: throws when the user is operating with a
|
|
54
|
+
* default-insecure HMAC key AND has opted into strict enforcement
|
|
55
|
+
* via MNEME_NEMESIS_STRICT=1. Call this from any code path that
|
|
56
|
+
* issues forensic-grade receipts (EU stamp / cli-activity / SIBYL).
|
|
57
|
+
*
|
|
58
|
+
* Non-strict mode: returns { ok: false, message } so caller can decide.
|
|
59
|
+
*/
|
|
60
|
+
export declare function strictKeyCheck(repoRoot: string): {
|
|
61
|
+
ok: boolean;
|
|
62
|
+
usingDefault: boolean;
|
|
63
|
+
message: string;
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=key_setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key_setup.d.ts","sourceRoot":"","sources":["../../src/nemesis/key_setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAUH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,SAAS,GAAG,QAAQ,CAAC;IACvF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB;oEACgE;IAChE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,iEAAiE;IACjE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAYD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,eAAe,CAyDjE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAiBpG;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAiCxG"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.53.0 — HMAC KEY SETUP WIZARD + STRICT mode.
|
|
3
|
+
*
|
|
4
|
+
* Closes P0-1 from the v2.52 session audit: pre-v2.53 NEMESIS warned LOUD
|
|
5
|
+
* when falling back to the public default key, but didn't auto-fix it
|
|
6
|
+
* + didn't refuse to operate. Result: every developer who ignored the
|
|
7
|
+
* warning shipped HMAC receipts an attacker can forge.
|
|
8
|
+
*
|
|
9
|
+
* v2.53 protocol:
|
|
10
|
+
* 1. First-run wizard auto-generates a 32-byte random key + writes it
|
|
11
|
+
* to .mneme/nemesis/hmac.key with 0o600 perms (owner-only).
|
|
12
|
+
* 2. MNEME_NEMESIS_STRICT=1 env var → key_management refuses to return
|
|
13
|
+
* the default-insecure key (throws instead). For CI / production /
|
|
14
|
+
* regulated environments.
|
|
15
|
+
* 3. Idempotent: re-running detects existing key + skips generation.
|
|
16
|
+
*
|
|
17
|
+
* Defensive: never throws on filesystem errors (best-effort), only the
|
|
18
|
+
* STRICT-mode refusal is intentional.
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, statSync, chmodSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import { randomBytes } from "node:crypto";
|
|
23
|
+
import { homedir } from "node:os";
|
|
24
|
+
const REPO_KEY_REL = ".mneme/nemesis/hmac.key";
|
|
25
|
+
const USER_KEY_REL = ".mneme/nemesis/hmac.key";
|
|
26
|
+
function keyPath(opts) {
|
|
27
|
+
if (opts.target === "user")
|
|
28
|
+
return join(homedir(), USER_KEY_REL);
|
|
29
|
+
return join(opts.repoRoot, REPO_KEY_REL);
|
|
30
|
+
}
|
|
31
|
+
function envHasValidKey() {
|
|
32
|
+
const k = process.env["MNEME_NEMESIS_KEY"];
|
|
33
|
+
return typeof k === "string" && k.length >= 16;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Run the key setup wizard. Idempotent; never overwrites an existing
|
|
37
|
+
* key unless force=true. Returns structured outcome.
|
|
38
|
+
*/
|
|
39
|
+
export function runKeyWizard(opts) {
|
|
40
|
+
try {
|
|
41
|
+
if (envHasValidKey() && !opts.force) {
|
|
42
|
+
const env = process.env["MNEME_NEMESIS_KEY"];
|
|
43
|
+
return {
|
|
44
|
+
ok: true,
|
|
45
|
+
action: "skipped",
|
|
46
|
+
keyLength: env.length,
|
|
47
|
+
reason: "MNEME_NEMESIS_KEY already set — no file action needed",
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const p = keyPath(opts);
|
|
51
|
+
if (existsSync(p) && !opts.force) {
|
|
52
|
+
try {
|
|
53
|
+
const k = readFileSync(p, "utf8").trim();
|
|
54
|
+
if (k.length >= 16) {
|
|
55
|
+
return {
|
|
56
|
+
ok: true,
|
|
57
|
+
action: "already-present",
|
|
58
|
+
path: p,
|
|
59
|
+
keyLength: k.length,
|
|
60
|
+
reason: `existing key at ${p} (${k.length} chars)`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch { /* fall through to regenerate */ }
|
|
65
|
+
}
|
|
66
|
+
if (opts.dryRun) {
|
|
67
|
+
return {
|
|
68
|
+
ok: true,
|
|
69
|
+
action: opts.target === "user" ? "generated-user" : "generated-repo",
|
|
70
|
+
path: p,
|
|
71
|
+
keyLength: 64,
|
|
72
|
+
reason: `dry-run: would write a fresh 64-char hex key to ${p}`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Generate + write
|
|
76
|
+
const dir = p.replace(/[\/\\][^\/\\]+$/, "");
|
|
77
|
+
try {
|
|
78
|
+
mkdirSync(dir, { recursive: true });
|
|
79
|
+
}
|
|
80
|
+
catch { /* ok */ }
|
|
81
|
+
const key = randomBytes(32).toString("hex");
|
|
82
|
+
writeFileSync(p, key + "\n", { mode: 0o600 });
|
|
83
|
+
// Best-effort chmod (Windows ignores)
|
|
84
|
+
try {
|
|
85
|
+
chmodSync(p, 0o600);
|
|
86
|
+
}
|
|
87
|
+
catch { /* */ }
|
|
88
|
+
return {
|
|
89
|
+
ok: true,
|
|
90
|
+
action: opts.target === "user" ? "generated-user" : "generated-repo",
|
|
91
|
+
path: p,
|
|
92
|
+
keyLength: key.length,
|
|
93
|
+
reason: `generated fresh 64-char hex key at ${p} (mode 0600)`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
return {
|
|
98
|
+
ok: false,
|
|
99
|
+
action: "failed",
|
|
100
|
+
keyLength: 0,
|
|
101
|
+
reason: `wizard failed: ${e.message}`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Check key file permissions (Unix only). Returns warning on world-readable
|
|
107
|
+
* files. On Windows, returns ok=true with note since chmod is a no-op.
|
|
108
|
+
*/
|
|
109
|
+
export function checkKeyPermissions(repoRoot) {
|
|
110
|
+
const p = join(repoRoot, REPO_KEY_REL);
|
|
111
|
+
if (!existsSync(p))
|
|
112
|
+
return { ok: true, reason: "no key file present" };
|
|
113
|
+
try {
|
|
114
|
+
const s = statSync(p);
|
|
115
|
+
const mode = s.mode & 0o777;
|
|
116
|
+
if (process.platform === "win32") {
|
|
117
|
+
return { ok: true, mode, reason: "Windows: chmod is a no-op; key access controlled by NTFS ACLs" };
|
|
118
|
+
}
|
|
119
|
+
// Owner-only is the goal: 0o600
|
|
120
|
+
if (mode === 0o600 || mode === 0o400) {
|
|
121
|
+
return { ok: true, mode, reason: `mode ${mode.toString(8)} — owner-only (correct)` };
|
|
122
|
+
}
|
|
123
|
+
return { ok: false, mode, reason: `mode ${mode.toString(8)} is too permissive; should be 600 (owner read/write only)` };
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
return { ok: false, reason: `stat failed: ${e.message}` };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* STRICT MODE check: throws when the user is operating with a
|
|
131
|
+
* default-insecure HMAC key AND has opted into strict enforcement
|
|
132
|
+
* via MNEME_NEMESIS_STRICT=1. Call this from any code path that
|
|
133
|
+
* issues forensic-grade receipts (EU stamp / cli-activity / SIBYL).
|
|
134
|
+
*
|
|
135
|
+
* Non-strict mode: returns { ok: false, message } so caller can decide.
|
|
136
|
+
*/
|
|
137
|
+
export function strictKeyCheck(repoRoot) {
|
|
138
|
+
// Resolve via the same path key_management uses.
|
|
139
|
+
const env = process.env["MNEME_NEMESIS_KEY"];
|
|
140
|
+
if (typeof env === "string" && env.length >= 16) {
|
|
141
|
+
return { ok: true, usingDefault: false, message: "MNEME_NEMESIS_KEY env var set" };
|
|
142
|
+
}
|
|
143
|
+
const repo = join(repoRoot, REPO_KEY_REL);
|
|
144
|
+
if (existsSync(repo)) {
|
|
145
|
+
try {
|
|
146
|
+
const k = readFileSync(repo, "utf8").trim();
|
|
147
|
+
if (k.length >= 16)
|
|
148
|
+
return { ok: true, usingDefault: false, message: `repo key at ${repo}` };
|
|
149
|
+
}
|
|
150
|
+
catch { /* */ }
|
|
151
|
+
}
|
|
152
|
+
const user = join(homedir(), USER_KEY_REL);
|
|
153
|
+
if (existsSync(user)) {
|
|
154
|
+
try {
|
|
155
|
+
const k = readFileSync(user, "utf8").trim();
|
|
156
|
+
if (k.length >= 16)
|
|
157
|
+
return { ok: true, usingDefault: false, message: `user key at ${user}` };
|
|
158
|
+
}
|
|
159
|
+
catch { /* */ }
|
|
160
|
+
}
|
|
161
|
+
const strict = process.env["MNEME_NEMESIS_STRICT"] === "1";
|
|
162
|
+
if (strict) {
|
|
163
|
+
throw new Error("STRICT MODE: MNEME_NEMESIS_STRICT=1 set + no production key configured. " +
|
|
164
|
+
`Run: node -e "require('@mneme-ai/core').nemesis.runKeyWizard({repoRoot:'${repoRoot}'})" ` +
|
|
165
|
+
"OR set MNEME_NEMESIS_KEY env var (≥16 chars).");
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
ok: false,
|
|
169
|
+
usingDefault: true,
|
|
170
|
+
message: "using default-insecure HMAC key — receipts are forgeable. Set MNEME_NEMESIS_STRICT=1 to enforce.",
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=key_setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key_setup.js","sourceRoot":"","sources":["../../src/nemesis/key_setup.ts"],"names":[],"mappings":"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"}
|