chainlesschain 0.47.8 → 0.49.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.
Files changed (86) hide show
  1. package/bin/chainlesschain.js +0 -0
  2. package/package.json +10 -8
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AppLayout-6SPt_8Y_.js → AppLayout-Rvi759IS.js} +1 -1
  5. package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +1 -0
  6. package/src/assets/web-panel/assets/{Dashboard-Br7kCwKJ.js → Dashboard-DBhFxXYQ.js} +2 -2
  7. package/src/assets/web-panel/assets/{index-tN-8TosE.js → index-uL0cZ8N_.js} +2 -2
  8. package/src/assets/web-panel/index.html +2 -2
  9. package/src/commands/activitypub.js +533 -0
  10. package/src/commands/codegen.js +303 -0
  11. package/src/commands/collab.js +482 -0
  12. package/src/commands/compliance.js +597 -6
  13. package/src/commands/crosschain.js +382 -0
  14. package/src/commands/dbevo.js +388 -0
  15. package/src/commands/dev.js +411 -0
  16. package/src/commands/federation.js +427 -0
  17. package/src/commands/fusion.js +332 -0
  18. package/src/commands/governance.js +505 -0
  19. package/src/commands/hardening.js +110 -0
  20. package/src/commands/incentive.js +373 -0
  21. package/src/commands/inference.js +304 -0
  22. package/src/commands/infra.js +361 -0
  23. package/src/commands/kg.js +371 -0
  24. package/src/commands/marketplace.js +326 -0
  25. package/src/commands/matrix.js +283 -0
  26. package/src/commands/mcp.js +441 -18
  27. package/src/commands/nlprog.js +329 -0
  28. package/src/commands/nostr.js +196 -7
  29. package/src/commands/ops.js +408 -0
  30. package/src/commands/perception.js +385 -0
  31. package/src/commands/pqc.js +34 -0
  32. package/src/commands/privacy.js +345 -0
  33. package/src/commands/quantization.js +280 -0
  34. package/src/commands/recommend.js +336 -0
  35. package/src/commands/reputation.js +349 -0
  36. package/src/commands/runtime.js +500 -0
  37. package/src/commands/sla.js +352 -0
  38. package/src/commands/social.js +265 -0
  39. package/src/commands/stress.js +252 -0
  40. package/src/commands/tech.js +268 -0
  41. package/src/commands/tenant.js +576 -0
  42. package/src/commands/trust.js +366 -0
  43. package/src/harness/mcp-client.js +330 -54
  44. package/src/index.js +114 -0
  45. package/src/lib/activitypub-bridge.js +623 -0
  46. package/src/lib/aiops.js +523 -0
  47. package/src/lib/autonomous-developer.js +524 -0
  48. package/src/lib/code-agent.js +442 -0
  49. package/src/lib/collaboration-governance.js +556 -0
  50. package/src/lib/community-governance.js +649 -0
  51. package/src/lib/compliance-framework-reporter.js +600 -0
  52. package/src/lib/content-recommendation.js +600 -0
  53. package/src/lib/cross-chain.js +669 -0
  54. package/src/lib/dbevo.js +669 -0
  55. package/src/lib/decentral-infra.js +445 -0
  56. package/src/lib/federation-hardening.js +587 -0
  57. package/src/lib/hardening-manager.js +409 -0
  58. package/src/lib/inference-network.js +407 -0
  59. package/src/lib/knowledge-graph.js +530 -0
  60. package/src/lib/matrix-bridge.js +252 -0
  61. package/src/lib/mcp-client.js +3 -0
  62. package/src/lib/mcp-registry.js +347 -0
  63. package/src/lib/mcp-scaffold.js +385 -0
  64. package/src/lib/multimodal.js +698 -0
  65. package/src/lib/nl-programming.js +595 -0
  66. package/src/lib/nostr-bridge.js +214 -38
  67. package/src/lib/perception.js +500 -0
  68. package/src/lib/pqc-manager.js +141 -9
  69. package/src/lib/privacy-computing.js +575 -0
  70. package/src/lib/protocol-fusion.js +535 -0
  71. package/src/lib/quantization.js +362 -0
  72. package/src/lib/reputation-optimizer.js +509 -0
  73. package/src/lib/skill-marketplace.js +397 -0
  74. package/src/lib/sla-manager.js +484 -0
  75. package/src/lib/social-graph.js +408 -0
  76. package/src/lib/stix-parser.js +167 -0
  77. package/src/lib/stress-tester.js +383 -0
  78. package/src/lib/tech-learning-engine.js +651 -0
  79. package/src/lib/tenant-saas.js +831 -0
  80. package/src/lib/threat-intel.js +268 -0
  81. package/src/lib/token-incentive.js +513 -0
  82. package/src/lib/topic-classifier.js +400 -0
  83. package/src/lib/trust-security.js +473 -0
  84. package/src/lib/ueba.js +403 -0
  85. package/src/lib/universal-runtime.js +771 -0
  86. package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +0 -1
@@ -0,0 +1,403 @@
1
+ /**
2
+ * UEBA — User and Entity Behavior Analytics.
3
+ *
4
+ * Pure analytics over a stream of `{entity, action, resource,
5
+ * timestamp, success?}` events. No DB dependency — callers load
6
+ * events from `audit_log` (or anywhere else) and feed them in.
7
+ *
8
+ * Three primary surfaces:
9
+ *
10
+ * buildBaseline(events) — summarize per-entity behaviour
11
+ * scoreEvent(baseline, event) — anomaly score 0–1 vs. baseline
12
+ * detectAnomalies(baseline, candidates, {threshold})
13
+ * rankEntities(events, {topK}) — highest-risk entities overall
14
+ *
15
+ * Scoring is intentionally simple (frequency-based surprise +
16
+ * failure signal) — enough to surface "this user logged in at 3am
17
+ * from a never-seen resource" without dragging in ML baggage.
18
+ */
19
+
20
+ /* ── helpers ───────────────────────────────────────────────── */
21
+
22
+ function _toHour(ts) {
23
+ if (ts == null) return null;
24
+ const d = typeof ts === "number" ? new Date(ts) : new Date(String(ts));
25
+ if (Number.isNaN(d.getTime())) return null;
26
+ return d.getUTCHours();
27
+ }
28
+
29
+ function _increment(map, key) {
30
+ if (key == null) return;
31
+ map.set(key, (map.get(key) || 0) + 1);
32
+ }
33
+
34
+ function _toObject(map) {
35
+ const out = {};
36
+ for (const [k, v] of map) out[k] = v;
37
+ return out;
38
+ }
39
+
40
+ /* ── buildBaseline ─────────────────────────────────────────── */
41
+
42
+ /**
43
+ * Build per-entity baselines from an event stream. Returns a Map of
44
+ * entityId → baseline. Each baseline is a frozen object; re-running
45
+ * `buildBaseline` on an extended event set returns new baselines
46
+ * rather than mutating prior results (important for watch loops).
47
+ */
48
+ export function buildBaseline(events) {
49
+ if (!Array.isArray(events)) return new Map();
50
+
51
+ const perEntity = new Map();
52
+
53
+ for (const ev of events) {
54
+ if (!ev || !ev.entity) continue;
55
+ let b = perEntity.get(ev.entity);
56
+ if (!b) {
57
+ b = {
58
+ entity: ev.entity,
59
+ eventCount: 0,
60
+ successCount: 0,
61
+ failureCount: 0,
62
+ actionCounts: new Map(),
63
+ resourceCounts: new Map(),
64
+ hourCounts: new Array(24).fill(0),
65
+ firstSeen: null,
66
+ lastSeen: null,
67
+ };
68
+ perEntity.set(ev.entity, b);
69
+ }
70
+
71
+ b.eventCount += 1;
72
+ if (ev.success === false) b.failureCount += 1;
73
+ else b.successCount += 1;
74
+
75
+ _increment(b.actionCounts, ev.action);
76
+ _increment(b.resourceCounts, ev.resource);
77
+
78
+ const h = _toHour(ev.timestamp);
79
+ if (h != null) b.hourCounts[h] += 1;
80
+
81
+ if (ev.timestamp != null) {
82
+ const t =
83
+ typeof ev.timestamp === "number"
84
+ ? ev.timestamp
85
+ : new Date(ev.timestamp).getTime();
86
+ if (!Number.isNaN(t)) {
87
+ if (b.firstSeen == null || t < b.firstSeen) b.firstSeen = t;
88
+ if (b.lastSeen == null || t > b.lastSeen) b.lastSeen = t;
89
+ }
90
+ }
91
+ }
92
+
93
+ // Freeze a serializable snapshot. Keep the Map-typed counts
94
+ // internally for O(1) scoring, plus JSON-friendly mirrors on the
95
+ // returned object.
96
+ for (const b of perEntity.values()) {
97
+ b.uniqueActions = b.actionCounts.size;
98
+ b.uniqueResources = b.resourceCounts.size;
99
+ b.failureRate = b.eventCount === 0 ? 0 : b.failureCount / b.eventCount;
100
+ b.actions = _toObject(b.actionCounts);
101
+ b.resources = _toObject(b.resourceCounts);
102
+ }
103
+
104
+ return perEntity;
105
+ }
106
+
107
+ /**
108
+ * Serialize a baseline Map to a plain-object dict suitable for
109
+ * `JSON.stringify` / DB persistence. Drops the internal Map copies.
110
+ */
111
+ export function serializeBaseline(baselineMap) {
112
+ const out = {};
113
+ for (const [entity, b] of baselineMap) {
114
+ out[entity] = {
115
+ entity: b.entity,
116
+ eventCount: b.eventCount,
117
+ successCount: b.successCount,
118
+ failureCount: b.failureCount,
119
+ failureRate: b.failureRate,
120
+ uniqueActions: b.uniqueActions,
121
+ uniqueResources: b.uniqueResources,
122
+ hourCounts: b.hourCounts.slice(),
123
+ actions: { ...b.actions },
124
+ resources: { ...b.resources },
125
+ firstSeen: b.firstSeen,
126
+ lastSeen: b.lastSeen,
127
+ };
128
+ }
129
+ return out;
130
+ }
131
+
132
+ /**
133
+ * Reverse of `serializeBaseline` — hydrate a persisted dict back
134
+ * into the runtime Map with `actionCounts` / `resourceCounts` Maps
135
+ * restored, so `scoreEvent` can use them directly.
136
+ */
137
+ export function deserializeBaseline(dict) {
138
+ const map = new Map();
139
+ if (!dict || typeof dict !== "object") return map;
140
+ for (const [entity, b] of Object.entries(dict)) {
141
+ const rebuilt = {
142
+ entity,
143
+ eventCount: b.eventCount || 0,
144
+ successCount: b.successCount || 0,
145
+ failureCount: b.failureCount || 0,
146
+ failureRate: b.failureRate || 0,
147
+ uniqueActions: b.uniqueActions || 0,
148
+ uniqueResources: b.uniqueResources || 0,
149
+ hourCounts:
150
+ Array.isArray(b.hourCounts) && b.hourCounts.length === 24
151
+ ? b.hourCounts.slice()
152
+ : new Array(24).fill(0),
153
+ actions: { ...(b.actions || {}) },
154
+ resources: { ...(b.resources || {}) },
155
+ actionCounts: new Map(Object.entries(b.actions || {})),
156
+ resourceCounts: new Map(Object.entries(b.resources || {})),
157
+ firstSeen: b.firstSeen ?? null,
158
+ lastSeen: b.lastSeen ?? null,
159
+ };
160
+ map.set(entity, rebuilt);
161
+ }
162
+ return map;
163
+ }
164
+
165
+ /* ── scoreEvent ────────────────────────────────────────────── */
166
+
167
+ /**
168
+ * Score a single event against an entity's baseline. Returns
169
+ * { score: 0..1, reasons: string[] }
170
+ *
171
+ * Higher score = more anomalous. A baseline with zero prior events
172
+ * is treated as "fully unseen" — every incoming event scores 1.
173
+ */
174
+ export function scoreEvent(baseline, event) {
175
+ if (!event) return { score: 0, reasons: [] };
176
+ if (!baseline || baseline.eventCount === 0) {
177
+ return { score: 1, reasons: ["no prior activity for entity"] };
178
+ }
179
+
180
+ const total = baseline.eventCount;
181
+ const reasons = [];
182
+
183
+ // Hour surprise
184
+ const h = _toHour(event.timestamp);
185
+ let hourSurprise = 0;
186
+ if (h != null) {
187
+ const hc = baseline.hourCounts[h] || 0;
188
+ hourSurprise = 1 - hc / total;
189
+ if (hc === 0) reasons.push(`unseen hour ${h}:00 UTC`);
190
+ else if (hourSurprise > 0.9) reasons.push(`rare hour ${h}:00 UTC`);
191
+ }
192
+
193
+ // Action surprise
194
+ const ac = baseline.actionCounts.get(event.action) || 0;
195
+ const actionSurprise = ac === 0 ? 1 : 1 - ac / total;
196
+ if (ac === 0 && event.action) reasons.push(`unseen action "${event.action}"`);
197
+ else if (actionSurprise > 0.9 && event.action)
198
+ reasons.push(`rare action "${event.action}"`);
199
+
200
+ // Resource surprise
201
+ const rc = baseline.resourceCounts.get(event.resource) || 0;
202
+ const resourceSurprise = rc === 0 ? 1 : 1 - rc / total;
203
+ if (rc === 0 && event.resource)
204
+ reasons.push(`unseen resource "${event.resource}"`);
205
+ else if (resourceSurprise > 0.9 && event.resource)
206
+ reasons.push(`rare resource "${event.resource}"`);
207
+
208
+ const base = (hourSurprise + actionSurprise + resourceSurprise) / 3;
209
+
210
+ // Failure signal — a failure against a mostly-successful baseline
211
+ // is notable even if every other feature looks normal.
212
+ let failureBonus = 0;
213
+ if (event.success === false && baseline.failureRate < 0.1) {
214
+ failureBonus = 0.3;
215
+ reasons.push("failure against low-failure baseline");
216
+ }
217
+
218
+ const score = Math.min(1, base + failureBonus);
219
+ return { score, reasons };
220
+ }
221
+
222
+ /* ── detectAnomalies ───────────────────────────────────────── */
223
+
224
+ /**
225
+ * Score each candidate event against the relevant entity baseline,
226
+ * and return those whose score meets `threshold` (default 0.7).
227
+ * Returns `[{event, score, reasons}]` sorted by descending score.
228
+ *
229
+ * `baselineMap` is the Map returned by `buildBaseline` (or rehydrated
230
+ * via `deserializeBaseline`).
231
+ */
232
+ export function detectAnomalies(baselineMap, candidateEvents, options = {}) {
233
+ const { threshold = 0.7 } = options;
234
+ if (!Array.isArray(candidateEvents)) return [];
235
+ if (!(baselineMap instanceof Map)) return [];
236
+
237
+ const out = [];
238
+ for (const ev of candidateEvents) {
239
+ if (!ev || !ev.entity) continue;
240
+ const baseline = baselineMap.get(ev.entity);
241
+ const result = scoreEvent(baseline, ev);
242
+ if (result.score >= threshold) {
243
+ out.push({ event: ev, score: result.score, reasons: result.reasons });
244
+ }
245
+ }
246
+
247
+ out.sort((a, b) => b.score - a.score);
248
+ return out;
249
+ }
250
+
251
+ /* ── rankEntities ──────────────────────────────────────────── */
252
+
253
+ /**
254
+ * Rank entities by composite risk derived directly from an event
255
+ * stream (no prior baseline required). Useful for the "top risky
256
+ * users this week" view.
257
+ *
258
+ * riskScore (0–100) = 40·failureRate
259
+ * + 30·uniqueResourceRatio
260
+ * + 30·burstiness
261
+ *
262
+ * where `burstiness` = max-hour / eventCount (1 means "all events
263
+ * fell into a single hour", 0.04 means "evenly spread".)
264
+ */
265
+ export function rankEntities(events, options = {}) {
266
+ const { topK = 10 } = options;
267
+ const baseline = buildBaseline(events);
268
+ const rows = [];
269
+
270
+ for (const b of baseline.values()) {
271
+ const uniqueResourceRatio =
272
+ b.eventCount === 0 ? 0 : b.uniqueResources / b.eventCount;
273
+ const maxHour = Math.max(...b.hourCounts);
274
+ const burstiness = b.eventCount === 0 ? 0 : maxHour / b.eventCount;
275
+ const riskScore =
276
+ 100 *
277
+ (0.4 * b.failureRate + 0.3 * uniqueResourceRatio + 0.3 * burstiness);
278
+ rows.push({
279
+ entity: b.entity,
280
+ eventCount: b.eventCount,
281
+ failureRate: b.failureRate,
282
+ uniqueResources: b.uniqueResources,
283
+ uniqueActions: b.uniqueActions,
284
+ burstiness,
285
+ riskScore: Math.round(riskScore * 100) / 100,
286
+ });
287
+ }
288
+
289
+ rows.sort((a, b) => b.riskScore - a.riskScore);
290
+ return topK > 0 ? rows.slice(0, topK) : rows;
291
+ }
292
+
293
+ /* ── persistence helpers ──────────────────────────────────── */
294
+
295
+ export function ensureUebaTables(db) {
296
+ db.exec(`
297
+ CREATE TABLE IF NOT EXISTS ueba_baselines (
298
+ entity TEXT PRIMARY KEY,
299
+ event_count INTEGER NOT NULL,
300
+ failure_rate REAL NOT NULL,
301
+ unique_resources INTEGER NOT NULL,
302
+ payload TEXT NOT NULL,
303
+ first_seen INTEGER,
304
+ last_seen INTEGER,
305
+ updated_at TEXT DEFAULT (datetime('now'))
306
+ )
307
+ `);
308
+ }
309
+
310
+ /**
311
+ * Persist a baseline Map (output of `buildBaseline`) into
312
+ * `ueba_baselines`. Rows are upserted keyed by entity.
313
+ */
314
+ export function saveBaselines(db, baselineMap) {
315
+ ensureUebaTables(db);
316
+ const serialized = serializeBaseline(baselineMap);
317
+ const insert = db.prepare(
318
+ `INSERT INTO ueba_baselines
319
+ (entity, event_count, failure_rate, unique_resources, payload,
320
+ first_seen, last_seen, updated_at)
321
+ VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'))`,
322
+ );
323
+ const update = db.prepare(
324
+ `UPDATE ueba_baselines
325
+ SET event_count = ?,
326
+ failure_rate = ?,
327
+ unique_resources = ?,
328
+ payload = ?,
329
+ first_seen = ?,
330
+ last_seen = ?,
331
+ updated_at = datetime('now')
332
+ WHERE entity = ?`,
333
+ );
334
+ const selectExisting = db.prepare(
335
+ `SELECT entity FROM ueba_baselines WHERE entity = ?`,
336
+ );
337
+
338
+ let saved = 0;
339
+ for (const [entity, b] of Object.entries(serialized)) {
340
+ const payload = JSON.stringify(b);
341
+ const prior = selectExisting.get(entity);
342
+ if (prior) {
343
+ update.run(
344
+ b.eventCount,
345
+ b.failureRate,
346
+ b.uniqueResources,
347
+ payload,
348
+ b.firstSeen ?? null,
349
+ b.lastSeen ?? null,
350
+ entity,
351
+ );
352
+ } else {
353
+ insert.run(
354
+ entity,
355
+ b.eventCount,
356
+ b.failureRate,
357
+ b.uniqueResources,
358
+ payload,
359
+ b.firstSeen ?? null,
360
+ b.lastSeen ?? null,
361
+ );
362
+ }
363
+ saved += 1;
364
+ }
365
+ return saved;
366
+ }
367
+
368
+ /**
369
+ * Load a previously saved baseline for a single entity. Returns the
370
+ * fully-hydrated baseline object (with Maps restored) or null if no
371
+ * row exists.
372
+ */
373
+ export function loadBaseline(db, entity) {
374
+ ensureUebaTables(db);
375
+ const row = db
376
+ .prepare(`SELECT payload FROM ueba_baselines WHERE entity = ?`)
377
+ .get(entity);
378
+ if (!row) return null;
379
+ try {
380
+ const dict = { [entity]: JSON.parse(row.payload) };
381
+ const map = deserializeBaseline(dict);
382
+ return map.get(entity) || null;
383
+ } catch {
384
+ return null;
385
+ }
386
+ }
387
+
388
+ /**
389
+ * Load all saved baselines into a Map<entity, baseline>.
390
+ */
391
+ export function loadAllBaselines(db) {
392
+ ensureUebaTables(db);
393
+ const rows = db.prepare(`SELECT entity, payload FROM ueba_baselines`).all();
394
+ const dict = {};
395
+ for (const r of rows) {
396
+ try {
397
+ dict[r.entity] = JSON.parse(r.payload);
398
+ } catch {
399
+ // Skip malformed rows — better to ignore than poison the Map.
400
+ }
401
+ }
402
+ return deserializeBaseline(dict);
403
+ }