ciphermesh 2.8.0 → 2.10.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.
@@ -0,0 +1,640 @@
1
+ {
2
+ "$comment": "Generated by scripts/generate-commands.mjs — do not edit. Which commands exist comes from the case labels in the two controllers; prose comes from README.md and the Ctrl+K palette.",
3
+ "counts": {
4
+ "total": 67,
5
+ "relay": 67,
6
+ "p2p": 64
7
+ },
8
+ "groups": [
9
+ {
10
+ "id": "essentials",
11
+ "name": "Essentials",
12
+ "commands": [
13
+ {
14
+ "name": "/help",
15
+ "args": "",
16
+ "summary": "All commands",
17
+ "modes": [
18
+ "relay",
19
+ "p2p"
20
+ ]
21
+ },
22
+ {
23
+ "name": "/invite",
24
+ "args": "[host:port]",
25
+ "summary": "Generate a `ciphermesh://` invite + QR code",
26
+ "modes": [
27
+ "relay"
28
+ ]
29
+ },
30
+ {
31
+ "name": "/me",
32
+ "args": "<action>",
33
+ "summary": "Third-person action — _«ana is compiling»_",
34
+ "modes": [
35
+ "relay",
36
+ "p2p"
37
+ ]
38
+ },
39
+ {
40
+ "name": "/msg",
41
+ "args": "<nick> <text>",
42
+ "summary": "Private message (DM)",
43
+ "modes": [
44
+ "relay",
45
+ "p2p"
46
+ ]
47
+ },
48
+ {
49
+ "name": "/nick",
50
+ "args": "<new>",
51
+ "summary": "Change nickname (before joining — recovers from \"nickname taken\")",
52
+ "modes": [
53
+ "relay"
54
+ ]
55
+ },
56
+ {
57
+ "name": "/plugins",
58
+ "args": "",
59
+ "summary": "List loaded plugins and the commands they add",
60
+ "modes": [
61
+ "relay",
62
+ "p2p"
63
+ ]
64
+ },
65
+ {
66
+ "name": "/quit",
67
+ "args": "",
68
+ "summary": "Leave",
69
+ "modes": [
70
+ "relay",
71
+ "p2p"
72
+ ]
73
+ },
74
+ {
75
+ "name": "/reply",
76
+ "args": "<text>",
77
+ "summary": "Reply quoting the last received message",
78
+ "modes": [
79
+ "relay",
80
+ "p2p"
81
+ ]
82
+ },
83
+ {
84
+ "name": "/tips",
85
+ "args": "",
86
+ "summary": "Show a rotating security/UX tip",
87
+ "modes": [
88
+ "relay",
89
+ "p2p"
90
+ ]
91
+ },
92
+ {
93
+ "name": "/users",
94
+ "args": "",
95
+ "summary": "Who's online (with away/status)",
96
+ "modes": [
97
+ "relay",
98
+ "p2p"
99
+ ]
100
+ },
101
+ {
102
+ "name": "/watch",
103
+ "args": "[add\\|remove\\|clear]",
104
+ "summary": "Alert on a keyword in **any** room, like a mention",
105
+ "modes": [
106
+ "relay",
107
+ "p2p"
108
+ ]
109
+ }
110
+ ]
111
+ },
112
+ {
113
+ "id": "rooms",
114
+ "name": "Rooms",
115
+ "commands": [
116
+ {
117
+ "name": "/ban",
118
+ "args": "",
119
+ "summary": "Owner moderation — bound to the public key, so a rename does not undo a ban",
120
+ "modes": [
121
+ "relay",
122
+ "p2p"
123
+ ]
124
+ },
125
+ {
126
+ "name": "/block",
127
+ "args": "",
128
+ "summary": "Stop seeing someone, **just for you**. Nothing is sent, the relay never learns, and they are not told — so anyone can use it, including in `general`, which has no owner. Works in P2P too, where there is no moderation at all.",
129
+ "modes": [
130
+ "relay",
131
+ "p2p"
132
+ ]
133
+ },
134
+ {
135
+ "name": "/blocklist",
136
+ "args": "",
137
+ "summary": "Stop seeing someone, **just for you**. Nothing is sent, the relay never learns, and they are not told — so anyone can use it, including in `general`, which has no owner. Works in P2P too, where there is no moderation at all.",
138
+ "modes": [
139
+ "relay",
140
+ "p2p"
141
+ ]
142
+ },
143
+ {
144
+ "name": "/create",
145
+ "args": "<room> <password>",
146
+ "summary": "Create a **private room** 🔒 — see below",
147
+ "modes": [
148
+ "relay"
149
+ ]
150
+ },
151
+ {
152
+ "name": "/join",
153
+ "args": "<room> [password]",
154
+ "summary": "Open a room as a **new buffer** — you stay in your other rooms (IRC style)",
155
+ "modes": [
156
+ "relay",
157
+ "p2p"
158
+ ]
159
+ },
160
+ {
161
+ "name": "/kick",
162
+ "args": "",
163
+ "summary": "Owner moderation — bound to the public key, so a rename does not undo a ban",
164
+ "modes": [
165
+ "relay",
166
+ "p2p"
167
+ ]
168
+ },
169
+ {
170
+ "name": "/leave",
171
+ "args": "[room]",
172
+ "summary": "Leave a room; its buffer closes (the last room is protected)",
173
+ "modes": [
174
+ "relay",
175
+ "p2p"
176
+ ]
177
+ },
178
+ {
179
+ "name": "/mute",
180
+ "args": "",
181
+ "summary": "Owner moderation — bound to the public key, so a rename does not undo a ban",
182
+ "modes": [
183
+ "relay",
184
+ "p2p"
185
+ ]
186
+ },
187
+ {
188
+ "name": "/owner",
189
+ "args": "",
190
+ "summary": "Room owner",
191
+ "modes": [
192
+ "relay",
193
+ "p2p"
194
+ ]
195
+ },
196
+ {
197
+ "name": "/room",
198
+ "args": "",
199
+ "summary": "Current room + your buffer list",
200
+ "modes": [
201
+ "relay",
202
+ "p2p"
203
+ ]
204
+ },
205
+ {
206
+ "name": "/rooms",
207
+ "args": "",
208
+ "summary": "List rooms (🔒 marks private ones)",
209
+ "modes": [
210
+ "relay",
211
+ "p2p"
212
+ ]
213
+ },
214
+ {
215
+ "name": "/topic",
216
+ "args": "[text\\|clear]",
217
+ "summary": "Show or set the room topic — shown in the status bar and synced to whoever joins later",
218
+ "modes": [
219
+ "relay",
220
+ "p2p"
221
+ ]
222
+ },
223
+ {
224
+ "name": "/unblock",
225
+ "args": "",
226
+ "summary": "Stop seeing someone, **just for you**. Nothing is sent, the relay never learns, and they are not told — so anyone can use it, including in `general`, which has no owner. Works in P2P too, where there is no moderation at all.",
227
+ "modes": [
228
+ "relay",
229
+ "p2p"
230
+ ]
231
+ }
232
+ ]
233
+ },
234
+ {
235
+ "id": "trust-security",
236
+ "name": "Trust & security",
237
+ "commands": [
238
+ {
239
+ "name": "/audit",
240
+ "args": "[n]",
241
+ "summary": "Local audit log",
242
+ "modes": [
243
+ "relay",
244
+ "p2p"
245
+ ]
246
+ },
247
+ {
248
+ "name": "/autolock",
249
+ "args": "",
250
+ "summary": "Lock the screen behind the session passphrase — manually or after idle time (`autoLock` in config). Privacy for the \"stepped away\" moment; `/panic` is for the worst one",
251
+ "modes": [
252
+ "relay",
253
+ "p2p"
254
+ ]
255
+ },
256
+ {
257
+ "name": "/backup",
258
+ "args": "[path]",
259
+ "summary": "Encrypted backup of identity + verified peers (restore at startup)",
260
+ "modes": [
261
+ "relay",
262
+ "p2p"
263
+ ]
264
+ },
265
+ {
266
+ "name": "/contacts",
267
+ "args": "[add\\|remove\\|all]",
268
+ "summary": "Contact book — persistent aliases on trust records (\"this fingerprint is João\"); shows in `/users`, rides along in identity backups",
269
+ "modes": [
270
+ "relay",
271
+ "p2p"
272
+ ]
273
+ },
274
+ {
275
+ "name": "/cover",
276
+ "args": "[on\\|constant\\|off]",
277
+ "summary": "Cover traffic — `on` = jittered decoys, `constant` = steady-rate paced channel",
278
+ "modes": [
279
+ "relay",
280
+ "p2p"
281
+ ]
282
+ },
283
+ {
284
+ "name": "/deniable",
285
+ "args": "[on\\|off]",
286
+ "summary": "Plausible-deniability mode",
287
+ "modes": [
288
+ "relay",
289
+ "p2p"
290
+ ]
291
+ },
292
+ {
293
+ "name": "/ephemeral",
294
+ "args": "<30s\\|5m\\|1h\\|off>",
295
+ "summary": "Self-destructing messages",
296
+ "modes": [
297
+ "relay",
298
+ "p2p"
299
+ ]
300
+ },
301
+ {
302
+ "name": "/fingerprint",
303
+ "args": "[nick]",
304
+ "summary": "Key fingerprint + a deterministic **randomart** picture of the key",
305
+ "modes": [
306
+ "relay",
307
+ "p2p"
308
+ ]
309
+ },
310
+ {
311
+ "name": "/lock",
312
+ "args": "",
313
+ "summary": "Lock the screen behind the session passphrase — manually or after idle time (`autoLock` in config). Privacy for the \"stepped away\" moment; `/panic` is for the worst one",
314
+ "modes": [
315
+ "relay",
316
+ "p2p"
317
+ ]
318
+ },
319
+ {
320
+ "name": "/panic",
321
+ "args": "[yes]",
322
+ "summary": "Duress wipe — securely erase all on-disk secrets (session, history, trust, keys) and exit",
323
+ "modes": [
324
+ "relay",
325
+ "p2p"
326
+ ]
327
+ },
328
+ {
329
+ "name": "/receipts",
330
+ "args": "[on\\|off]",
331
+ "summary": "Send read receipts (✓✓)",
332
+ "modes": [
333
+ "relay",
334
+ "p2p"
335
+ ]
336
+ },
337
+ {
338
+ "name": "/theme",
339
+ "args": "[name]",
340
+ "summary": "Nick colour theme: neon, matrix, mono, sunset, ocean",
341
+ "modes": [
342
+ "relay",
343
+ "p2p"
344
+ ]
345
+ },
346
+ {
347
+ "name": "/trust",
348
+ "args": "",
349
+ "summary": "Accept new key / trust status",
350
+ "modes": [
351
+ "relay",
352
+ "p2p"
353
+ ]
354
+ },
355
+ {
356
+ "name": "/trustlist",
357
+ "args": "",
358
+ "summary": "Accept new key / trust status",
359
+ "modes": [
360
+ "relay",
361
+ "p2p"
362
+ ]
363
+ },
364
+ {
365
+ "name": "/verify",
366
+ "args": "<nick>",
367
+ "summary": "SAS code (~40-bit) + QR + key randomart for out-of-band verification",
368
+ "modes": [
369
+ "relay",
370
+ "p2p"
371
+ ]
372
+ },
373
+ {
374
+ "name": "/verify-confirm",
375
+ "args": "<nick>",
376
+ "summary": "Mark peer as verified",
377
+ "modes": [
378
+ "relay",
379
+ "p2p"
380
+ ]
381
+ }
382
+ ]
383
+ },
384
+ {
385
+ "id": "history-files",
386
+ "name": "History & files",
387
+ "commands": [
388
+ {
389
+ "name": "/accept",
390
+ "args": "",
391
+ "summary": "Accept / decline an incoming file offer",
392
+ "modes": [
393
+ "relay",
394
+ "p2p"
395
+ ]
396
+ },
397
+ {
398
+ "name": "/doctor",
399
+ "args": "[host:port]",
400
+ "summary": "Diagnose why a connection fails: address, DNS, TCP port, TLS (CA vs self-signed) and protocol version — each failure with what to do about it",
401
+ "modes": [
402
+ "relay",
403
+ "p2p"
404
+ ]
405
+ },
406
+ {
407
+ "name": "/export",
408
+ "args": "[path]",
409
+ "summary": "Export history as .txt or .json (plaintext!)",
410
+ "modes": [
411
+ "relay",
412
+ "p2p"
413
+ ]
414
+ },
415
+ {
416
+ "name": "/file",
417
+ "args": "<path>",
418
+ "summary": "Offer a file (≤ 50MB) — the recipient must `/accept`; transfers resume",
419
+ "modes": [
420
+ "relay",
421
+ "p2p"
422
+ ]
423
+ },
424
+ {
425
+ "name": "/find",
426
+ "args": "[term]",
427
+ "summary": "Search **this room's scrollback** and press Enter to **jump to the message**, highlighted",
428
+ "modes": [
429
+ "relay",
430
+ "p2p"
431
+ ]
432
+ },
433
+ {
434
+ "name": "/history",
435
+ "args": "[n]",
436
+ "summary": "Last n messages from history",
437
+ "modes": [
438
+ "relay",
439
+ "p2p"
440
+ ]
441
+ },
442
+ {
443
+ "name": "/img",
444
+ "args": "[path]",
445
+ "summary": "Render the last received image in **full resolution** (kitty/iTerm2)",
446
+ "modes": [
447
+ "relay",
448
+ "p2p"
449
+ ]
450
+ },
451
+ {
452
+ "name": "/play",
453
+ "args": "[path]",
454
+ "summary": "Play the last received voice note (`afplay`/`sox`/`ffplay`)",
455
+ "modes": [
456
+ "relay",
457
+ "p2p"
458
+ ]
459
+ },
460
+ {
461
+ "name": "/reject",
462
+ "args": "",
463
+ "summary": "Accept / decline an incoming file offer",
464
+ "modes": [
465
+ "relay",
466
+ "p2p"
467
+ ]
468
+ },
469
+ {
470
+ "name": "/retention",
471
+ "args": "<7d\\|24h\\|30m>",
472
+ "summary": "Purge local history older than the given age",
473
+ "modes": [
474
+ "relay",
475
+ "p2p"
476
+ ]
477
+ },
478
+ {
479
+ "name": "/search",
480
+ "args": "<term>",
481
+ "summary": "Search the encrypted local history (on disk, across sessions)",
482
+ "modes": [
483
+ "relay",
484
+ "p2p"
485
+ ]
486
+ },
487
+ {
488
+ "name": "/voice",
489
+ "args": "[secs]",
490
+ "summary": "Record & send an encrypted voice note (needs `sox`/`ffmpeg`; default 10s)",
491
+ "modes": [
492
+ "relay",
493
+ "p2p"
494
+ ]
495
+ }
496
+ ]
497
+ },
498
+ {
499
+ "id": "presence-fun",
500
+ "name": "Presence & fun",
501
+ "commands": [
502
+ {
503
+ "name": "/autoaway",
504
+ "args": "<minutes\\|off>",
505
+ "summary": "Go away by itself after that long without typing (1–240 minutes)",
506
+ "modes": [
507
+ "relay",
508
+ "p2p"
509
+ ]
510
+ },
511
+ {
512
+ "name": "/away",
513
+ "args": "",
514
+ "summary": "Mark yourself away — while away, unreads are counted (`[away · N new]`) and `/back` shows a summary",
515
+ "modes": [
516
+ "relay",
517
+ "p2p"
518
+ ]
519
+ },
520
+ {
521
+ "name": "/back",
522
+ "args": "",
523
+ "summary": "Mark yourself away — while away, unreads are counted (`[away · N new]`) and `/back` shows a summary",
524
+ "modes": [
525
+ "relay",
526
+ "p2p"
527
+ ]
528
+ },
529
+ {
530
+ "name": "/clear",
531
+ "args": "",
532
+ "summary": "Clear the chat",
533
+ "modes": [
534
+ "relay",
535
+ "p2p"
536
+ ]
537
+ },
538
+ {
539
+ "name": "/delete",
540
+ "args": "",
541
+ "summary": "Edit or delete your last message — the **original line is rewritten in place** (marked _(edited)_) or replaced by a tombstone, instead of a new line you have to mentally staple to it",
542
+ "modes": [
543
+ "relay",
544
+ "p2p"
545
+ ]
546
+ },
547
+ {
548
+ "name": "/dnd",
549
+ "args": "[on\\|off\\|mentions\\|HH:MM-HH:MM]",
550
+ "summary": "Do-not-disturb, mentions-only, or quiet hours",
551
+ "modes": [
552
+ "relay",
553
+ "p2p"
554
+ ]
555
+ },
556
+ {
557
+ "name": "/edit",
558
+ "args": "",
559
+ "summary": "Edit or delete your last message — the **original line is rewritten in place** (marked _(edited)_) or replaced by a tombstone, instead of a new line you have to mentally staple to it",
560
+ "modes": [
561
+ "relay",
562
+ "p2p"
563
+ ]
564
+ },
565
+ {
566
+ "name": "/mentions",
567
+ "args": "[n]",
568
+ "summary": "Recent mentions of you this session (who, where, when)",
569
+ "modes": [
570
+ "relay",
571
+ "p2p"
572
+ ]
573
+ },
574
+ {
575
+ "name": "/notify",
576
+ "args": "",
577
+ "summary": "Sound / desktop notifications",
578
+ "modes": [
579
+ "relay",
580
+ "p2p"
581
+ ]
582
+ },
583
+ {
584
+ "name": "/pin",
585
+ "args": "",
586
+ "summary": "Pin messages",
587
+ "modes": [
588
+ "relay",
589
+ "p2p"
590
+ ]
591
+ },
592
+ {
593
+ "name": "/pins",
594
+ "args": "",
595
+ "summary": "Pin messages",
596
+ "modes": [
597
+ "relay",
598
+ "p2p"
599
+ ]
600
+ },
601
+ {
602
+ "name": "/react",
603
+ "args": "<emoji>",
604
+ "summary": "React to the last message — the emoji lands **on the message**, with a count when several people react",
605
+ "modes": [
606
+ "relay",
607
+ "p2p"
608
+ ]
609
+ },
610
+ {
611
+ "name": "/sound",
612
+ "args": "",
613
+ "summary": "Sound / desktop notifications",
614
+ "modes": [
615
+ "relay",
616
+ "p2p"
617
+ ]
618
+ },
619
+ {
620
+ "name": "/status",
621
+ "args": "<text\\|off>",
622
+ "summary": "Free-form status — emojis welcome (`/status :fire: coding`)",
623
+ "modes": [
624
+ "relay",
625
+ "p2p"
626
+ ]
627
+ },
628
+ {
629
+ "name": "/unpin",
630
+ "args": "",
631
+ "summary": "Pin messages",
632
+ "modes": [
633
+ "relay",
634
+ "p2p"
635
+ ]
636
+ }
637
+ ]
638
+ }
639
+ ]
640
+ }
package/docs/demo.svg CHANGED
@@ -18,7 +18,7 @@
18
18
  <rect x="0" y="82" width="720" height="26" fill="#1a1a2e"/>
19
19
  <text x="16" y="100">
20
20
  <tspan fill="#27c93f">●</tspan><tspan fill="#ffffff" font-weight="bold"> CipherMesh</tspan>
21
- <tspan fill="#555"> │ </tspan><tspan fill="#ffffff" font-weight="bold">felipe</tspan>
21
+ <tspan fill="#555"> │ </tspan><tspan fill="#ffffff" font-weight="bold">ana</tspan>
22
22
  <tspan fill="#8888aa"> ● 3 online │ </tspan><tspan fill="#00ff9f">E2E</tspan>
23
23
  </text>
24
24
 
@@ -28,7 +28,7 @@
28
28
  <text x="16" y="192"><tspan fill="#8888aa"> ↩ bob: "is the crypto actually real?"</tspan></text>
29
29
 
30
30
  <!-- own message, right aligned -->
31
- <text x="704" y="218" text-anchor="end"><tspan fill="#ffffff" font-weight="bold">felipebob</tspan><tspan fill="#ffffff">: X25519 + XSalsa20-Poly1305 🔒</tspan> <tspan fill="#00ff9f">✓✓</tspan> <tspan fill="#ffffff">[14:33]</tspan></text>
31
+ <text x="704" y="218" text-anchor="end"><tspan fill="#ffffff" font-weight="bold">anarui</tspan><tspan fill="#ffffff">: X25519 + XSalsa20-Poly1305 🔒</tspan> <tspan fill="#00ff9f">✓✓</tspan> <tspan fill="#ffffff">[14:33]</tspan></text>
32
32
 
33
33
  <!-- system lines -->
34
34
  <text x="16" y="244"><tspan fill="#ffffff">[14:33] * 🔒 Secure channel with ana — E2E established</tspan></text>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ciphermesh",
3
- "version": "2.8.0",
3
+ "version": "2.10.0",
4
4
  "description": "Secure terminal chat for the local network (LAN) with real end-to-end encryption (E2EE) using libsodium",
5
5
  "type": "module",
6
6
  "main": "src/client/index.js",
@@ -49,7 +49,8 @@
49
49
  "docker:build": "docker compose build",
50
50
  "docker:up": "docker compose up -d",
51
51
  "docker:down": "docker compose down",
52
- "docker:logs": "docker compose logs -f"
52
+ "docker:logs": "docker compose logs -f",
53
+ "commands:build": "node scripts/generate-commands.mjs"
53
54
  },
54
55
  "dependencies": {
55
56
  "@noble/post-quantum": "0.6.1",