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
@@ -4,6 +4,14 @@
4
4
  */
5
5
 
6
6
  import crypto from "crypto";
7
+ import {
8
+ generatePrivateKey as _genPriv,
9
+ getPublicKey as _getPub,
10
+ signEvent as _signEvent,
11
+ verifyEvent as _verifyEvent,
12
+ npubEncode as _npubEncode,
13
+ nsecEncode as _nsecEncode,
14
+ } from "@chainlesschain/session-core/nostr-crypto";
7
15
 
8
16
  /* ── In-memory stores ──────────────────────────────────────── */
9
17
  const _relays = new Map();
@@ -88,36 +96,71 @@ export function addRelay(db, url) {
88
96
 
89
97
  /* ── Event Publishing ─────────────────────────────────────── */
90
98
 
91
- export function publishEvent(db, kind, content, pubkey, tags) {
99
+ /**
100
+ * Publish a NIP-01 event. When `privateKey` is provided the event is
101
+ * schnorr-signed with BIP-340 and `pubkey` is derived from it (any
102
+ * caller-supplied `pubkey` is ignored and overridden to match the key).
103
+ * Without a private key the caller gets an *unsigned* event — useful
104
+ * for in-memory workflows (e.g. `cc nostr publish` from REPL demos) but
105
+ * such events will be rejected by real relays.
106
+ */
107
+ export function publishEvent(db, kind, content, pubkey, tags, privateKey) {
92
108
  if (content === undefined || content === null)
93
109
  throw new Error("Content is required");
94
110
 
95
- const now = new Date().toISOString();
96
- const serialized = JSON.stringify([
97
- 0,
98
- pubkey || "anonymous",
99
- now,
100
- kind || 1,
101
- tags || [],
102
- content,
103
- ]);
104
- const id = crypto.createHash("sha256").update(serialized).digest("hex");
105
- const sig = crypto.randomBytes(64).toString("hex");
111
+ const createdAt = Math.floor(Date.now() / 1000);
112
+ const resolvedKind = kind || EVENT_KINDS.TEXT_NOTE;
113
+ const resolvedTags = tags || [];
114
+
115
+ let event;
116
+ if (privateKey) {
117
+ const derivedPubkey = _getPub(privateKey);
118
+ event = _signEvent(
119
+ {
120
+ pubkey: derivedPubkey,
121
+ created_at: createdAt,
122
+ kind: resolvedKind,
123
+ tags: resolvedTags,
124
+ content,
125
+ },
126
+ privateKey,
127
+ );
128
+ } else {
129
+ // Unsigned path: compute a deterministic id from canonical serialization
130
+ // so dedup still works, but leave sig empty to signal "not signed".
131
+ const fallbackPubkey = pubkey || "anonymous";
132
+ const serialized = JSON.stringify([
133
+ 0,
134
+ fallbackPubkey,
135
+ createdAt,
136
+ resolvedKind,
137
+ resolvedTags,
138
+ content,
139
+ ]);
140
+ const id = crypto.createHash("sha256").update(serialized).digest("hex");
141
+ event = {
142
+ id,
143
+ pubkey: fallbackPubkey,
144
+ created_at: createdAt,
145
+ kind: resolvedKind,
146
+ tags: resolvedTags,
147
+ content,
148
+ sig: "",
149
+ };
150
+ }
106
151
 
107
- const event = {
108
- id,
109
- pubkey: pubkey || "anonymous",
110
- kind: kind || EVENT_KINDS.TEXT_NOTE,
111
- content,
112
- tags: tags || [],
113
- sig,
114
- createdAt: now,
152
+ const storedEvent = {
153
+ id: event.id,
154
+ pubkey: event.pubkey,
155
+ kind: event.kind,
156
+ content: event.content,
157
+ tags: event.tags,
158
+ sig: event.sig,
159
+ createdAt: event.created_at,
115
160
  relayUrl: null,
116
161
  };
162
+ _events.set(event.id, storedEvent);
117
163
 
118
- _events.set(id, event);
119
-
120
- // Count relays that received the event
121
164
  const writeRelays = [..._relays.values()].filter((r) => r.writeEnabled);
122
165
  let sentCount = 0;
123
166
  for (const relay of writeRelays) {
@@ -129,18 +172,35 @@ export function publishEvent(db, kind, content, pubkey, tags) {
129
172
  `INSERT INTO nostr_events (id, pubkey, kind, content, tags, sig, created_at, relay_url, imported)
130
173
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
131
174
  ).run(
132
- id,
175
+ event.id,
133
176
  event.pubkey,
134
177
  event.kind,
135
178
  content,
136
- JSON.stringify(tags || []),
137
- sig,
138
- now,
179
+ JSON.stringify(resolvedTags),
180
+ event.sig,
181
+ new Date(createdAt * 1000).toISOString(),
139
182
  null,
140
183
  0,
141
184
  );
142
185
 
143
- return { success: true, event, sentCount };
186
+ return { success: true, event: storedEvent, sentCount };
187
+ }
188
+
189
+ /**
190
+ * Verify an event's schnorr signature + id integrity (NIP-01).
191
+ * Returns false for unsigned events (empty `sig`).
192
+ */
193
+ export function verifyEventSignature(event) {
194
+ if (!event || !event.sig) return false;
195
+ return _verifyEvent({
196
+ id: event.id,
197
+ pubkey: event.pubkey,
198
+ created_at: event.createdAt ?? event.created_at,
199
+ kind: event.kind,
200
+ tags: event.tags,
201
+ content: event.content,
202
+ sig: event.sig,
203
+ });
144
204
  }
145
205
 
146
206
  /* ── Event Retrieval ──────────────────────────────────────── */
@@ -157,21 +217,17 @@ export function getEvents(filter = {}) {
157
217
  /* ── Keypair Generation ───────────────────────────────────── */
158
218
 
159
219
  export function generateKeypair() {
160
- const privateKey = crypto.randomBytes(32).toString("hex");
161
- const publicKey = crypto
162
- .createHash("sha256")
163
- .update(privateKey)
164
- .digest("hex");
165
- const id = crypto.randomUUID();
166
-
220
+ const privateKey = _genPriv();
221
+ const publicKey = _getPub(privateKey);
167
222
  const keypair = {
168
- id,
223
+ id: crypto.randomUUID(),
169
224
  publicKey,
170
225
  privateKey,
226
+ npub: _npubEncode(publicKey),
227
+ nsec: _nsecEncode(privateKey),
171
228
  createdAt: new Date().toISOString(),
172
229
  };
173
- _keypairs.set(id, keypair);
174
-
230
+ _keypairs.set(keypair.id, keypair);
175
231
  return keypair;
176
232
  }
177
233
 
@@ -185,6 +241,126 @@ export function mapDid(did, nostrPubkey) {
185
241
  return { did, nostrPubkey, mapped: true };
186
242
  }
187
243
 
244
+ /* ── NIP-04: Encrypted Direct Messages ─────────────────────── */
245
+
246
+ /**
247
+ * Compute NIP-04 ECDH shared secret.
248
+ * Nostr pubkeys are x-only (32 bytes); prepend 0x02 to reconstruct a
249
+ * compressed point. Point negation yields the same x-coordinate, so the
250
+ * y-sign does not affect the shared secret.
251
+ */
252
+ function _computeSharedSecret(privKeyHex, pubKeyHex) {
253
+ const ecdh = crypto.createECDH("secp256k1");
254
+ ecdh.setPrivateKey(Buffer.from(privKeyHex, "hex"));
255
+ const compressedPub = Buffer.concat([
256
+ Buffer.from([0x02]),
257
+ Buffer.from(pubKeyHex, "hex"),
258
+ ]);
259
+ return ecdh.computeSecret(compressedPub);
260
+ }
261
+
262
+ /**
263
+ * Publish a NIP-04 encrypted direct message (kind=4).
264
+ * @param {Object} db - SQLite database handle
265
+ * @param {Object} params - { senderPrivkey, senderPubkey, recipientPubkey, plaintext }
266
+ * @returns {Object} { success, event, sentCount }
267
+ */
268
+ export function publishDirectMessage(
269
+ db,
270
+ { senderPrivkey, senderPubkey, recipientPubkey, plaintext },
271
+ ) {
272
+ if (!senderPrivkey || !senderPubkey || !recipientPubkey) {
273
+ throw new Error(
274
+ "senderPrivkey, senderPubkey, and recipientPubkey are required",
275
+ );
276
+ }
277
+ if (plaintext === undefined || plaintext === null) {
278
+ throw new Error("plaintext is required");
279
+ }
280
+
281
+ const sharedSecret = _computeSharedSecret(senderPrivkey, recipientPubkey);
282
+ const iv = crypto.randomBytes(16);
283
+ const cipher = crypto.createCipheriv("aes-256-cbc", sharedSecret, iv);
284
+ const encrypted = Buffer.concat([
285
+ cipher.update(plaintext, "utf8"),
286
+ cipher.final(),
287
+ ]);
288
+ const content = `${encrypted.toString("base64")}?iv=${iv.toString("base64")}`;
289
+
290
+ return publishEvent(db, EVENT_KINDS.ENCRYPTED_DM, content, senderPubkey, [
291
+ ["p", recipientPubkey],
292
+ ]);
293
+ }
294
+
295
+ /**
296
+ * Decrypt a NIP-04 direct message event.
297
+ * @param {Object} params - { event, recipientPrivkey }
298
+ * @returns {string} decrypted plaintext
299
+ */
300
+ export function decryptDirectMessage({ event, recipientPrivkey }) {
301
+ if (!event || !event.content || !event.pubkey) {
302
+ throw new Error("event with content and pubkey is required");
303
+ }
304
+ if (!recipientPrivkey) {
305
+ throw new Error("recipientPrivkey is required");
306
+ }
307
+ if (event.kind !== EVENT_KINDS.ENCRYPTED_DM) {
308
+ throw new Error(
309
+ `Expected kind=${EVENT_KINDS.ENCRYPTED_DM}, got kind=${event.kind}`,
310
+ );
311
+ }
312
+
313
+ const parts = event.content.split("?iv=");
314
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
315
+ throw new Error("Invalid NIP-04 content format");
316
+ }
317
+
318
+ const ciphertext = Buffer.from(parts[0], "base64");
319
+ const iv = Buffer.from(parts[1], "base64");
320
+ const sharedSecret = _computeSharedSecret(recipientPrivkey, event.pubkey);
321
+ const decipher = crypto.createDecipheriv("aes-256-cbc", sharedSecret, iv);
322
+ const decrypted = Buffer.concat([
323
+ decipher.update(ciphertext),
324
+ decipher.final(),
325
+ ]);
326
+ return decrypted.toString("utf8");
327
+ }
328
+
329
+ /* ── NIP-09: Event Deletion Request ─────────────────────────── */
330
+
331
+ /**
332
+ * Publish a NIP-09 deletion request (kind=5) referencing prior events.
333
+ * @param {Object} db - SQLite database handle
334
+ * @param {Object} params - { eventIds: string[], reason?: string, pubkey?: string }
335
+ */
336
+ export function publishDeletion(db, { eventIds, reason = "", pubkey }) {
337
+ if (!Array.isArray(eventIds) || eventIds.length === 0) {
338
+ throw new Error("eventIds must be a non-empty array");
339
+ }
340
+ const tags = eventIds.map((id) => ["e", id]);
341
+ return publishEvent(db, EVENT_KINDS.DELETE, reason, pubkey, tags);
342
+ }
343
+
344
+ /* ── NIP-25: Reactions ──────────────────────────────────────── */
345
+
346
+ /**
347
+ * Publish a NIP-25 reaction (kind=7) to another event.
348
+ * @param {Object} db - SQLite database handle
349
+ * @param {Object} params - { targetEventId, targetPubkey, content?, pubkey? }
350
+ */
351
+ export function publishReaction(
352
+ db,
353
+ { targetEventId, targetPubkey, content = "+", pubkey },
354
+ ) {
355
+ if (!targetEventId || !targetPubkey) {
356
+ throw new Error("targetEventId and targetPubkey are required");
357
+ }
358
+ return publishEvent(db, EVENT_KINDS.REACTION, content, pubkey, [
359
+ ["e", targetEventId],
360
+ ["p", targetPubkey],
361
+ ]);
362
+ }
363
+
188
364
  /* ── Reset (for testing) ───────────────────────────────────── */
189
365
 
190
366
  export function _resetState() {