ciphermesh 2.13.0 → 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.
@@ -1,4 +1,4 @@
1
- # SecureLAN Chat — Complete Technical Documentation
1
+ # CipherMesh — Complete Technical Documentation
2
2
 
3
3
  > Secure chat for a local area network (LAN) with real end-to-end encryption (E2EE).
4
4
  > The server **never** has access to the content of messages.
@@ -26,18 +26,18 @@
26
26
 
27
27
  ### What it is
28
28
 
29
- SecureLAN Chat is an instant messaging system designed to operate **exclusively within a local area network (LAN)**. It uses end-to-end encryption (E2EE) based on **Curve25519 + XSalsa20-Poly1305** (via libsodium), ensuring that the server acts only as a **blind relay** — it forwards bytes it cannot read.
29
+ CipherMesh is an instant messaging system designed to operate **exclusively within a local area network (LAN)**. It uses end-to-end encryption (E2EE) based on **Curve25519 + XSalsa20-Poly1305** (via libsodium), ensuring that the server acts only as a **blind relay** — it forwards bytes it cannot read.
30
30
 
31
31
  ### Core Principle
32
32
 
33
33
  ```
34
- Cliente A Servidor Cliente B
34
+ Client A Server Client B
35
35
  | | |
36
- |--- payload cifrado ----->| |
37
- | |--- payload cifrado ----->|
36
+ |--- encrypted payload --->| |
37
+ | |--- encrypted payload --->|
38
38
  | | |
39
- | O servidor NAO possui | |
40
- | a chave para decifrar | |
39
+ | The server does NOT | |
40
+ | hold the key to open it | |
41
41
  ```
42
42
 
43
43
  The server knows **only**:
@@ -55,15 +55,15 @@ The server **never** knows:
55
55
 
56
56
  ```
57
57
  ┌──────────────┐
58
- Servidor
59
- (Relay)
58
+ Server
59
+ (Relay)
60
60
  │ :3600 │
61
61
  └──────┬───────┘
62
62
  │ WebSocket
63
63
  ┌────────────┼────────────┐
64
64
  │ │ │
65
65
  ┌─────┴─────┐ ┌───┴───┐ ┌─────┴─────┐
66
- Cliente A │ │ ... │ │ Cliente N │
66
+ Client A │ │ ... │ │ Client N │
67
67
  │ (Terminal) │ │ │ │ (Terminal) │
68
68
  └───────────┘ └───────┘ └───────────┘
69
69
  ```
@@ -74,14 +74,14 @@ This is the **star** topology (star topology) — the default mode. All clients
74
74
 
75
75
  ```
76
76
  ┌───────────┐
77
- Cliente A │
77
+ Client A │
78
78
  │ (Terminal) │
79
79
  └─────┬─────┘
80
80
  │ WebSocket direto
81
81
  ┌─────────┼──────────┐
82
82
  │ │
83
83
  ┌───┴───┐ ┌────┴────┐
84
- │ ... │ │ Cliente N│
84
+ │ ... │ │ Client N
85
85
  │ │ │(Terminal)│
86
86
  └───────┘ └─────────┘
87
87
  ```
@@ -94,13 +94,24 @@ In P2P mode (`npm run p2p`), peers discover each other via mDNS on the LAN and c
94
94
 
95
95
  ### Production Dependencies
96
96
 
97
- | Package | Version | Role | Why this lib? |
98
- |--------|--------|-------|-------------------|
99
- | **ws** | ^8.18 | WebSocket server/client | The most mature and performant WebSocket implementation for Node.js. Zero dependencies. Natively supports binary frames (essential for encrypted payloads). Used by thousands of projects in production. |
100
- | **sodium-native** | ^4.3 | Cryptography | Native binding of **libsodium** for Node.js. Runs in compiled C (not pure JS), offering real performance and audited security. libsodium is considered the most secure and easy-to-use modern cryptographic library. Used by Signal, Discord, Wireguard. |
101
- | **blessed** | ^0.1.81 | Terminal UI | A library for building rich interfaces in the terminal. Supports layouts with boxes, inputs, scrolling, colors, borders — similar to ncurses but in JS. Lets you create a modern app-style UI without leaving the terminal. |
102
- | **chalk** | ^5.4 | Terminal colors | Colored text formatting in the terminal. Used to highlight nicks, timestamps, errors, status. ESM-only in v5 (compatible with our `"type": "module"`). |
103
- | **play-sound** | ^1.1 | Audio | Plays audio files (mp3) for sound notifications. Uses the OS's native players (mplayer, aplay, cmdmp3). |
97
+ <!-- The package list is checked against package.json by
98
+ test/architecture-doc.test.js. Versions deliberately live only in
99
+ package.json: a number copied here is a number that goes stale, and
100
+ pinning it would put a doc edit in the way of every dependency bump. -->
101
+
102
+ | Package | Role |
103
+ | --- | --- |
104
+ | **@noble/post-quantum** | ML-KEM-768 for the post-quantum hybrid handshake (`src/crypto/PQHybrid.js`). libsodium has no ML-KEM, and this one is audited. |
105
+ | **blessed** | Terminal UI — boxes, scrolling, colours, borders. ncurses-shaped, in JS. Its key parser is old enough to need a shim (`src/client/keyboard.js`). |
106
+ | **bonjour-service** | mDNS service discovery, so P2P peers find each other on the LAN with no relay (`src/p2p/Discovery.js`). |
107
+ | **boxen** | The framed server and P2P banners (`src/shared/banner.js`, `src/p2p/index.js`). |
108
+ | **chalk** | Coloured output outside the blessed UI — banners, prompts, the boot sequence. ESM-only in v5, which suits `"type": "module"`. |
109
+ | **gradient-string** | The gradient across the startup banner (`src/shared/banner.js`). |
110
+ | **jimp** | Decodes received images so they can be drawn as half-blocks in the chat (`src/client/ImagePreview.js`). |
111
+ | **node-notifier** | Desktop notifications. Driven only from `src/shared/notifyWorker.js` — see §4.2 for why it is kept at arm’s length. |
112
+ | **qrcode-terminal** | Renders `/invite` as a QR code in the terminal. |
113
+ | **sodium-native** | Native binding of **libsodium**. Runs in compiled C, not pure JS, so the security is audited and the secret memory is real — see the comparison below. |
114
+ | **ws** | The most mature and performant WebSocket implementation for Node.js. Zero dependencies. Natively supports binary frames (essential for encrypted payloads). |
104
115
 
105
116
  ### Why sodium-native and not tweetnacl?
106
117
 
@@ -121,11 +132,12 @@ In P2P mode (`npm run p2p`), peers discover each other via mDNS on the LAN and c
121
132
  ### Development Dependencies
122
133
 
123
134
  | Package | Role |
124
- |--------|-------|
125
- | **eslint** ^9.17 | Linting ensures code quality and consistency |
126
- | **@eslint/js** ^9.17 | ESLint's recommended base configuration |
127
- | **globals** ^15.14 | Globals definitions (node, browser) for ESLint |
128
- | **prettier** ^3.4 | Automatic formatting consistent code with no style debates |
135
+ | --- | --- |
136
+ | **@eslint/js** | ESLint's recommended base configuration. |
137
+ | **eslint** | Linting. |
138
+ | **figlet** | Not used at runtime: `test/banner.test.js` pins the committed ASCII banner against figlet’s own output, so the art cannot drift. |
139
+ | **globals** | Globals definitions (node) for ESLint. |
140
+ | **prettier** | Automatic formatting — consistent code with no style debates. |
129
141
 
130
142
  ### Node.js >= 20
131
143
 
@@ -140,71 +152,112 @@ Minimum requirement: Node.js 20 LTS. Reasons:
140
152
 
141
153
  ## 3. Directory Structure
142
154
 
155
+ <!-- Checked by test/architecture-doc.test.js: every module under src/ has to
156
+ appear here, and nothing here may name a file that does not exist. -->
157
+
143
158
  ```
144
- securelan-chat/
159
+ ciphermesh/
160
+
161
+ ├── bin/
162
+ │ └── ciphermesh.js # CLI entry point (npx ciphermesh)
145
163
 
146
164
  ├── docs/
147
- └── ARCHITECTURE.md # Este documento
165
+ ├── ARCHITECTURE.md
166
+ │ ├── PLUGINS.md
167
+ │ ├── PROTOCOL.md
168
+ │ ├── SETUP.md
169
+ │ └── commands.json
148
170
 
149
171
  ├── src/
150
172
  │ ├── server/
151
- │ │ ├── index.js # Entry point do servidor
152
- │ │ ├── WebSocketServer.js # Gerencia conexoes WebSocket
153
- │ │ ├── SessionManager.js # Controla sessoes ativas (clientes conectados)
154
- │ │ ├── MessageRouter.js # Roteia payloads cifrados entre clientes
155
- │ │ ├── OfflineQueue.js # Fila de mensagens para peers offline
156
- │ │ └── CertManager.js # Geracao e carregamento de certs TLS
173
+ │ │ ├── index.js # Server entry point
174
+ │ │ ├── CertManager.js # TLS certificate generation and loading
175
+ │ │ ├── config.js # Server configuration and env vars
176
+ │ │ ├── ConnectionGuard.js # Rate limits and connection abuse guards
177
+ │ │ ├── MessageRouter.js # Routes encrypted payloads between clients
178
+ │ │ ├── OfflineQueue.js # Queue for messages to offline peers
179
+ │ │ ├── preflight.js # Startup checks before the port is opened
180
+ │ │ ├── presence.js # Presence/hub counters
181
+ │ │ ├── SessionManager.js # Active sessions (connected clients)
182
+ │ │ └── WebSocketServer.js # WebSocket connection handling
157
183
  │ │
158
184
  │ ├── client/
159
- │ │ ├── index.js # Entry point do cliente
160
- │ │ ├── UI.js # Interface blessed (layout, rendering)
161
- │ │ ├── Connection.js # Conexao WebSocket com o servidor
162
- │ │ ├── ChatController.js # Logica central: conecta UI + Connection + Crypto
163
- │ │ └── FileTransfer.js # Envio/recepcao de arquivos cifrados (chunks)
185
+ │ │ ├── index.js # Client entry point
186
+ │ │ ├── ChatController.js # Core logic: UI + Connection + Crypto
187
+ │ │ ├── Connection.js # WebSocket connection to the server
188
+ │ │ ├── FileTransfer.js # Encrypted file send/receive (chunks)
189
+ │ │ ├── ImagePreview.js # Half-block image previews
190
+ │ │ ├── keyboard.js # Keyboard-protocol shim (Shift+Enter)
191
+ │ │ └── UI.js # Blessed interface (layout, wrapping, rendering)
164
192
  │ │
165
193
  │ ├── crypto/
166
- │ │ ├── KeyManager.js # Gera e gerencia pares de chaves (em memoria)
167
- │ │ ├── MessageCrypto.js # Cifra e decifra mensagens (crypto_box_easy)
168
- │ │ ├── Handshake.js # Protocolo de troca de chaves publicas
169
- │ │ ├── NonceManager.js # Geracao e validacao de nonces
170
- │ │ ├── DoubleRatchet.js # PFS via Double Ratchet (DH ratchet + KDF chains)
171
- │ │ ├── TrustStore.js # TOFU + SAS (persistencia de fingerprints)
172
- │ │ └── StateManager.js # Persistencia cifrada de estado (Argon2id + secretbox)
194
+ │ │ ├── CertPinStore.js # TLS certificate pinning
195
+ │ │ ├── DeniableEncrypt.js # Deniable (symmetric) message mode
196
+ │ │ ├── DeviceIdentity.js # Ed25519 identity and the signed device list
197
+ │ │ ├── DoubleRatchet.js # PFS via Double Ratchet (DH ratchet + KDF chains)
198
+ │ │ ├── Handshake.js # Public-key exchange protocol
199
+ │ │ ├── HistoryStore.js # Encrypted local history on disk
200
+ │ │ ├── IdentityBackup.js # Encrypted identity + trust export/import
201
+ │ │ ├── KeyManager.js # Key pairs (in memory)
202
+ │ │ ├── MessageCrypto.js # Encrypt/decrypt (crypto_box_easy)
203
+ │ │ ├── NonceManager.js # Nonce generation and replay checks
204
+ │ │ ├── PQHybrid.js # Post-quantum hybrid (X25519 + ML-KEM-768)
205
+ │ │ ├── RoomKey.js # Room key derivation and rotation
206
+ │ │ ├── SealedSender.js # Sealed sender — the relay cannot see who sent what
207
+ │ │ ├── SenderKey.js # Sender keys for group messages
208
+ │ │ ├── StateManager.js # Encrypted state persistence (Argon2id + secretbox)
209
+ │ │ └── TrustStore.js # TOFU + SAS (fingerprint persistence)
173
210
  │ │
174
211
  │ ├── p2p/
175
- │ │ ├── index.js # Entry point do modo P2P
176
- │ │ ├── Discovery.js # mDNS discovery via bonjour-service
177
- │ │ ├── PeerServer.js # WebSocket server local (porta aleatoria)
178
- │ │ ├── PeerConnectionManager.js # Gerencia conexoes outbound/inbound
179
- │ │ └── P2PChatController.js # Orquestrador P2P (crypto + UI + peers)
212
+ │ │ ├── index.js # P2P mode entry point
213
+ │ │ ├── Discovery.js # mDNS discovery via bonjour-service
214
+ │ │ ├── P2PChatController.js # P2P orchestrator (crypto + UI + peers)
215
+ │ │ ├── PeerConnectionManager.js # Outbound/inbound connection management
216
+ │ │ └── PeerServer.js # Local WebSocket server (random port)
180
217
  │ │
181
218
  │ ├── protocol/
182
- │ │ ├── messages.js # Definicao dos tipos de mensagem do protocolo
183
- │ │ └── validators.js # Validacao de estrutura dos payloads
219
+ │ │ ├── capabilities.js # Feature negotiation between versions
220
+ │ │ ├── messages.js # Protocol message types
221
+ │ │ └── validators.js # Payload structure validation
184
222
  │ │
185
223
  │ └── shared/
186
- │ ├── constants.js # Constantes globais (portas, limites, versao)
187
- └── logger.js # Logger estruturado (com niveis e timestamps)
188
-
189
- ├── test/
190
- ├── crypto.test.js # Testes do modulo criptografico
191
- ├── protocol.test.js # Testes de validacao do protocolo
192
- ├── nonce.test.js # Testes do gerenciador de nonces
193
- ├── integration.test.js # Testes de integracao E2E
194
- ├── double-ratchet.test.js # Testes do Double Ratchet
195
- ├── trust-store.test.js # Testes do TrustStore + SAS
196
- ├── message-crypto.test.js # Testes do MessageCrypto (padding, encrypt)
197
- └── state-manager.test.js # Testes de persistencia de estado
224
+ │ ├── AuditLog.js # Local audit trail
225
+ ├── banner.js # Startup banners
226
+ ├── commandSuggest.js # Did-you-mean for mistyped commands
227
+ ├── config.js # Client config file
228
+ ├── constants.js # Global constants (ports, limits, version)
229
+ ├── coverTraffic.js # Cover traffic (anti-metadata)
230
+ ├── desktopNotify.js # Desktop notifications breaker, throttle, isolation
231
+ ├── deviceProvisioning.js # Multi-device request/grant/accept
232
+ ├── dnd.js # Do-not-disturb / mentions-only gating
233
+ ├── doctor.js # Connection diagnosis for /doctor
234
+ ├── emoji.js # `:shortcode:` map
235
+ ├── fuzzy.js # Fuzzy matching for the palette and pickers
236
+ │ ├── invite.js # Invite strings and QR payloads
237
+ │ ├── keyArt.js # Fingerprint art
238
+ │ ├── lastSession.js # Last-session hints
239
+ │ ├── logger.js # Structured logger (levels and timestamps)
240
+ │ ├── notifyWorker.js # One-shot notification helper, spawned console-less
241
+ │ ├── onboarding.js # First-run setup wizard
242
+ │ ├── panic.js # Duress wipe
243
+ │ ├── pluginCommand.js # The /plugins command
244
+ │ ├── PluginManager.js # Plugin loading and sandboxing
245
+ │ ├── prompt.js # Readline prompts
246
+ │ ├── terminalGraphics.js # kitty/iTerm2 inline image protocols
247
+ │ ├── themes.js # Nick colour themes
248
+ │ ├── tips.js # Security/UX tips
249
+ │ ├── trust.js # Trust badges
250
+ │ └── voiceNote.js # Voice note record/playback
198
251
 
252
+ ├── test/ # 90 suites, run with `npm test`
199
253
  ├── scripts/
200
- └── generate-fingerprint.js # Utilitario: gera fingerprint de chave publica
254
+ ├── generate-commands.mjs # Regenerates docs/commands.json from the code
255
+ │ └── ...
201
256
 
202
- ├── .editorconfig
203
- ├── .eslintrc.js
204
- ├── .gitignore
205
- ├── .npmrc
257
+ ├── Formula/ciphermesh.rb # Homebrew formula
258
+ ├── eslint.config.js
206
259
  ├── .prettierrc
207
- ├── jsconfig.json
260
+ ├── CHANGELOG.md
208
261
  ├── package.json
209
262
  └── README.md
210
263
  ```
@@ -263,33 +316,67 @@ securelan-chat/
263
316
  - Connects to the server and starts the UI
264
317
 
265
318
  #### `src/client/UI.js` — Blessed Interface
266
- - Layout divided into 3 areas:
319
+ - Layout divided into 4 areas:
267
320
 
268
321
  ```
269
- ┌─────────────────────────────────────────┐
270
- SecureLAN Chat [3 online] E2E │ <- Header/Status bar
271
- ├─────────────────────────────────────────┤
272
-
273
- [10:30] Alice: Ola! │ <- Chat area (scrollable)
274
- [10:31] Voce: Oi Alice!
275
- [10:32] * Bob entrou no chat
276
- [10:32] Bob: Fala galera
277
-
278
- ├─────────────────────────────────────────┤
279
- > Digite sua mensagem... <- Input box
280
- └─────────────────────────────────────────┘
322
+ ┌──────────────────────────────────────────────────────┐
323
+ CipherMesh ▏ felipe ● 3 online E2E │ <- Header
324
+ ├──────────────────────────────────────────────────────┤
325
+ 10:30 🦊 ana <- Chat area
326
+ Hi! This message wraps well short of the │ (scrollable)
327
+ window, not at the border
328
+
329
+ │ 10:31 🐧 felipe ✓✓
330
+ ▎ Oi ana
331
+ │ │
332
+ 10:32 * bob entrou no chat
333
+ ├──────────────────────────────────────────────────────┤
334
+ │ #general Tab ~ Ctrl+K commands ~ /help ~ ^C │ <- Status bar
335
+ ├──────────────────────────────────────────────────────┤
336
+ │ > Type your message... │ <- Input box
337
+ └──────────────────────────────────────────────────────┘
281
338
  ```
282
339
 
283
- - The status bar shows: chat name, online users, E2E indicator
284
- - Chat area with automatic and manual scroll
285
- - Input with history (up/down arrows)
286
- - Distinct colors per user (chalk)
287
- - Special commands: `/quit`, `/users`, `/fingerprint`, `/clear`, `/file`, `/sound`, `/help`
340
+ - **Messages are blocks, not lines.** A header naming the sender, then the text
341
+ wrapped at 65 % of the window (78 columns at most) and indented under it. Each
342
+ entry is one `'\n'`-joined string so it stays a single addressable log line —
343
+ reactions, read receipts, edits and the ephemeral burn all still address it by
344
+ index.
345
+ - What distinguishes a message is a coloured rule down the left of the body,
346
+ not its alignment: yellow when it mentions you, magenta for a DM, the accent
347
+ for your own, nothing for a plain incoming one.
348
+ - Runs from one sender fold under a single header, but only inside the same
349
+ minute, so folding never costs the reader a timestamp.
350
+ - `wrapTagged` wraps text that already carries blessed tags: it closes the open
351
+ tag stack at each break and reopens it after, because blessed carries its
352
+ attribute state across the whole content and a tag left open would bleed into
353
+ the next line's gutter. Wrapping before the markdown pass would be simpler and
354
+ would split `**bold**` spans in half.
355
+ - Everything is laid out again on resize, active buffer and stored ones alike,
356
+ from a per-entry recipe kept alongside the rendered string. Entries with no
357
+ recipe — image previews, animation frames — keep exactly what they were given.
358
+ - The status bar shows the room, buffer tabs, and the key shortcuts
359
+ - Chat area with automatic and manual scroll, plus a "new messages ↓" pill
360
+ - Distinct colours per user, with an emoji avatar derived from the nickname
288
361
  - Animated "typing..." indicator with support for multiple peers
289
362
  - Sound notifications (toggle via `/sound on|off`)
290
363
  - Progress bar for file transfers
291
364
  - Notifications for users joining/leaving
292
365
 
366
+ #### `src/client/keyboard.js` — Keyboard protocols
367
+ - A terminal cannot tell Shift+Enter from Enter unless the application asks it
368
+ to, so startup requests the kitty keyboard protocol (`CSI > 1 u`) and xterm's
369
+ `modifyOtherKeys` level 1 (`CSI > 4 ; 1 m`), and undoes both on the way out.
370
+ - blessed's key parser cannot read what comes back — neither a `u` final byte
371
+ after two parameters nor a `~` after three — and would emit `13;2u` as five
372
+ typed characters. So the reports are decoded on the raw byte stream ahead of
373
+ it, installed as `blessed.screen({ input })`.
374
+ - Enter with any modifier becomes a newline; every other enhanced report is
375
+ rewritten to the legacy encoding blessed already understands; anything with no
376
+ legacy equivalent is dropped rather than typed; arrows, function keys, mouse
377
+ reports and bracketed pastes pass through untouched.
378
+ - `CIPHERMESH_LEGACY_KEYS=1` skips the shim and the negotiation entirely.
379
+
293
380
  #### `src/client/Connection.js` — WebSocket Client
294
381
  - Connects to the server via `ws`
295
382
  - Automatic reconnect with exponential backoff (1s, 2s, 4s, 8s, max 30s)
@@ -306,6 +393,12 @@ securelan-chat/
306
393
  - Validates fingerprints
307
394
  - Manages file transfers via FileTransfer
308
395
  - Sound notification when text messages are received
396
+ - Desktop notifications through `src/shared/desktopNotify.js`: rate-limited to
397
+ one per 3 s, muted for the session on the first refusal with one line in the
398
+ chat saying why, and on Windows delivered by a detached, console-less helper
399
+ (`src/shared/notifyWorker.js`) because SnoreToast writes its diagnostics to the
400
+ attached console — the one blessed is drawing on — when notifications are
401
+ disabled for the application
309
402
 
310
403
  #### `src/client/FileTransfer.js` — File Transfer
311
404
  - Sending: reads the file, splits it into 48KB chunks, encrypts each chunk E2E via broadcast
@@ -433,9 +526,9 @@ securelan-chat/
433
526
  Defines the protocol's message types. All messages have:
434
527
  ```js
435
528
  {
436
- type: string, // tipo da mensagem
437
- version: 1, // versao do protocolo
438
- timestamp: number // Date.now() do remetente
529
+ type: string, // message type
530
+ version: 1, // protocol version
531
+ timestamp: number // Date.now() on the sender
439
532
  }
440
533
  ```
441
534
 
@@ -520,7 +613,7 @@ export const FILE_CHUNK_SIZE = 49152; // 48KB
520
613
  "version": 1,
521
614
  "timestamp": 1739800000000,
522
615
  "nickname": "Alice",
523
- "publicKey": "base64(32 bytes da chave publica Curve25519)",
616
+ "publicKey": "base64(32-byte Curve25519 public key)",
524
617
  "caps": ["sk1"]
525
618
  }
526
619
  ```
@@ -541,7 +634,7 @@ client's.
541
634
  {
542
635
  "sessionId": "660e8400-e29b-41d4-a716-446655440001",
543
636
  "nickname": "Bob",
544
- "publicKey": "base64(chave publica do Bob)",
637
+ "publicKey": "base64(Bob's public key)",
545
638
  "caps": ["sk1"]
546
639
  }
547
640
  ],
@@ -666,7 +759,7 @@ Besides text messages, the encrypted payload may contain commands (the `action`
666
759
  "peer": {
667
760
  "sessionId": "770e8400-e29b-41d4-a716-446655440002",
668
761
  "nickname": "Charlie",
669
- "publicKey": "base64(chave publica do Charlie)"
762
+ "publicKey": "base64(Charlie's public key)"
670
763
  }
671
764
  }
672
765
  ```
@@ -719,12 +812,12 @@ This combination (known as **NaCl crypto_box**) was chosen because:
719
812
  ### 6.3 Key Generation
720
813
 
721
814
  ```
722
- 1. Cliente inicia
723
- 2. sodium.crypto_box_keypair() gera:
724
- - publicKey: 32 bytes (pode ser compartilhada)
725
- - secretKey: 32 bytes (NUNCA sai da memoria do processo)
726
- 3. Ambas armazenadas em sodium.sodium_malloc() (secure memory)
727
- 4. Fingerprint = SHA256(publicKey) formatada como XXXX:XXXX:XXXX:XXXX
815
+ 1. The client starts
816
+ 2. sodium.crypto_box_keypair() produces:
817
+ - publicKey: 32 bytes (safe to share)
818
+ - secretKey: 32 bytes (NEVER leaves the process's memory)
819
+ 3. Both held in sodium.sodium_malloc() (secure memory)
820
+ 4. Fingerprint = SHA256(publicKey), formatted as XXXX:XXXX:XXXX:XXXX
728
821
  ```
729
822
 
730
823
  ### 6.4 Authenticated Encryption with crypto_box_easy
@@ -734,12 +827,12 @@ This combination (known as **NaCl crypto_box**) was chosen because:
734
827
  ```
735
828
  crypto_box_easy(ciphertext, plaintext, nonce, recipientPublicKey, senderSecretKey)
736
829
 
737
- Internamente:
738
- 1. X25519 DH: sharedSecret = ECDH(recipientPub, senderSec)
830
+ Internally:
831
+ 1. X25519 DH: sharedSecret = ECDH(recipientPub, senderSec)
739
832
  2. Key derivation: encKey = HSalsa20(sharedSecret, zeros)
740
- 3. Cifra: XSalsa20(plaintext, nonce, encKey) -> ciphertext
741
- 4. MAC: Poly1305(ciphertext) -> tag de 16 bytes
742
- 5. Output: tag || ciphertext (autenticado)
833
+ 3. Encrypt: XSalsa20(plaintext, nonce, encKey) -> ciphertext
834
+ 4. MAC: Poly1305(ciphertext) -> 16-byte tag
835
+ 5. Output: tag || ciphertext (authenticated)
743
836
  ```
744
837
 
745
838
  The shared key is derived implicitly on each call. The DH guarantees
@@ -749,38 +842,38 @@ that both sides (Alice and Bob) arrive at the same secret without exchanging it
749
842
 
750
843
  ```
751
844
  Input:
752
- - plaintext: Buffer (mensagem em UTF-8)
753
- - nonce: 24 bytes (gerado pelo NonceManager)
754
- - recipientPublicKey: 32 bytes (chave publica do destinatario)
755
- - senderSecretKey: 32 bytes (chave secreta do remetente)
845
+ - plaintext: Buffer (UTF-8 message)
846
+ - nonce: 24 bytes (from the NonceManager)
847
+ - recipientPublicKey: 32 bytes (recipient's public key)
848
+ - senderSecretKey: 32 bytes (sender's secret key)
756
849
 
757
- Processo:
850
+ Process:
758
851
  ciphertext = crypto_box_easy(plaintext, nonce, recipientPublicKey, senderSecretKey)
759
852
 
760
853
  Output:
761
- - ciphertext: Buffer (plaintext.length + 16 bytes de MAC)
762
- - nonce: 24 bytes (enviado junto, nao e segredo)
854
+ - ciphertext: Buffer (plaintext.length + 16 bytes of MAC)
855
+ - nonce: 24 bytes (sent alongside; it is not a secret)
763
856
 
764
- Total enviado: ciphertext (N+16 bytes) + nonce (24 bytes)
857
+ Total on the wire: ciphertext (N+16 bytes) + nonce (24 bytes)
765
858
  ```
766
859
 
767
860
  ### 6.6 Message Decryption
768
861
 
769
862
  ```
770
863
  Input:
771
- - ciphertext: Buffer (recebido da rede)
772
- - nonce: 24 bytes (recebido da rede)
773
- - senderPublicKey: 32 bytes (chave publica do remetente)
774
- - recipientSecretKey: 32 bytes (chave secreta do destinatario)
864
+ - ciphertext: Buffer (off the network)
865
+ - nonce: 24 bytes (off the network)
866
+ - senderPublicKey: 32 bytes (sender's public key)
867
+ - recipientSecretKey: 32 bytes (recipient's secret key)
775
868
 
776
- Processo:
777
- 1. NonceManager valida que nonce nao foi usado antes (anti-replay)
869
+ Process:
870
+ 1. NonceManager checks the nonce has not been seen before (anti-replay)
778
871
  2. plaintext = crypto_box_open_easy(ciphertext, nonce, senderPublicKey, recipientSecretKey)
779
- 3. Se MAC invalido -> rejeita (mensagem foi adulterada)
780
- 4. Se MAC valido -> parse do JSON interno
872
+ 3. MAC invalid -> reject (the message was tampered with)
873
+ 4. MAC valid -> parse the inner JSON
781
874
 
782
875
  Output:
783
- - plaintext: Buffer (mensagem original)
876
+ - plaintext: Buffer (the original message)
784
877
  ```
785
878
 
786
879
  ### 6.7 Nonce Structure (24 bytes)
@@ -788,12 +881,12 @@ Output:
788
881
  ```
789
882
  ┌──────────────────┬──────────────┬──────────────────────┐
790
883
  │ Timestamp (8B) │ Counter (4B) │ Random (12B) │
791
- ms desde epoch sequencial │ sodium.randombytes │
884
+ ms since epoch │ sequential │ sodium.randombytes │
792
885
  └──────────────────┴──────────────┴──────────────────────┘
793
886
 
794
- - Timestamp: impede replay entre sessoes diferentes
795
- - Counter: garante ordenacao e unicidade dentro da sessao
796
- - Random: garante unicidade mesmo com clocks sincronizados
887
+ - Timestamp: blocks replay across different sessions
888
+ - Counter: guarantees ordering and uniqueness within a session
889
+ - Random: guarantees uniqueness even with synchronised clocks
797
890
  ```
798
891
 
799
892
  ### 6.8 Fingerprint Verification
@@ -801,16 +894,16 @@ Output:
801
894
  The fingerprint lets users verify each other's identity **out of band** (for example, in person or by phone):
802
895
 
803
896
  ```
804
- 1. Alice ve seu fingerprint: A1B2:C3D4:E5F6:7890
805
- 2. Bob ve o fingerprint de Alice: A1B2:C3D4:E5F6:7890
806
- 3. Bob confirma pessoalmente com Alice que os valores batem
807
- 4. Se nao baterem -> MITM detectado
897
+ 1. Alice reads her own fingerprint: A1B2:C3D4:E5F6:7890
898
+ 2. Bob reads Alice's fingerprint: A1B2:C3D4:E5F6:7890
899
+ 3. Bob confirms with Alice, in person, that the values match
900
+ 4. They do not match -> MITM detected
808
901
  ```
809
902
 
810
903
  The fingerprint is computed like this:
811
904
  ```
812
905
  fingerprint = SHA-256(publicKey)
813
- = primeiros 8 bytes, formatados em hex com separador ':'
906
+ = first 8 bytes, hex, ':'-separated
814
907
  = "A1B2:C3D4:E5F6:7890"
815
908
  ```
816
909
 
@@ -948,7 +1041,7 @@ multiply the fallback's cost by the number of devices per peer.
948
1041
  ### 7.1 Full Diagram
949
1042
 
950
1043
  ```
951
- Cliente A Servidor Cliente B
1044
+ Client A Server Client B
952
1045
  │ │ │
953
1046
  │ 1. JOIN(nick, pubKeyA) │ │
954
1047
  │ ──────────────────────────>│ │
@@ -964,7 +1057,7 @@ multiply the fallback's cost by the number of devices per peer.
964
1057
  │ usando pubKeyB + secKeyA │ usando pubKeyA + secKeyB
965
1058
  │ │ │
966
1059
  │ 6. ENCRYPTED_MSG ─────────│────────────────────────> │
967
- │ │ 7. Decifra com sharedKey│
1060
+ │ │ 7. Decrypt with sharedKey
968
1061
  │ │ │
969
1062
  ```
970
1063
 
@@ -995,24 +1088,24 @@ multiply the fallback's cost by the number of devices per peer.
995
1088
 
996
1089
  ## 8. Step-by-Step Communication Flow
997
1090
 
998
- ### 8.1 Full Scenario: Alice sends "Ola" to Bob
1091
+ ### 8.1 Full Scenario: Alice sends "Hi" to Bob
999
1092
 
1000
1093
  ```
1001
- TEMPO ACAO
1094
+ TIME ACTION
1002
1095
  ───── ──────────────────────────────────────────────────────
1003
- t0 Alice digita "Ola" no input e pressiona Enter
1096
+ t0 Alice types "Hi" in the composer and presses Enter
1004
1097
 
1005
- t1 ChatController recebe o texto da UI
1006
- ChatController verifica se tem sharedKey com Bob
1007
- Se nao tem -> erro "Handshake nao completado com Bob"
1098
+ t1 ChatController receives the text from the UI
1099
+ ChatController checks it has a sharedKey with Bob
1100
+ If it does not -> error "Handshake not completed with Bob"
1008
1101
 
1009
1102
  t2 MessageCrypto.encrypt():
1010
- - NonceManager gera nonce de 24 bytes
1011
- - Serializa payload interno: { text: "Ola", sentAt: t2, messageId: "a1b2" }
1103
+ - NonceManager produces a 24-byte nonce
1104
+ - Serialises the inner payload: { text: "Hi", sentAt: t2, messageId: "a1b2" }
1012
1105
  - crypto_box_easy_afternm(payload, nonce, sharedKeyAB)
1013
- - Retorna { ciphertext: Buffer, nonce: Buffer }
1106
+ - Returns { ciphertext: Buffer, nonce: Buffer }
1014
1107
 
1015
- t3 Connection envia ao servidor:
1108
+ t3 Connection sends to the server:
1016
1109
  {
1017
1110
  type: "encrypted_message",
1018
1111
  from: "alice-session-id",
@@ -1020,27 +1113,27 @@ t3 Connection envia ao servidor:
1020
1113
  payload: { ciphertext: "base64(...)", nonce: "base64(...)" }
1021
1114
  }
1022
1115
 
1023
- t4 Servidor (MessageRouter):
1024
- - Valida estrutura (tem type, from, to, payload)
1025
- - NAO abre payload
1026
- - Encontra WebSocket do Bob pelo sessionId
1027
- - Encaminha o JSON inteiro para Bob
1116
+ t4 Server (MessageRouter):
1117
+ - Validates the structure (has type, from, to, payload)
1118
+ - Does NOT open the payload
1119
+ - Finds Bob's WebSocket by sessionId
1120
+ - Forwards the whole JSON to Bob
1028
1121
 
1029
- t5 Bob (Connection) recebe o JSON
1030
- ChatController identifica: encrypted_message de Alice
1122
+ t5 Bob (Connection) receives the JSON
1123
+ ChatController identifies it: encrypted_message from Alice
1031
1124
 
1032
1125
  t6 MessageCrypto.decrypt():
1033
- - Extrai ciphertext e nonce do payload
1034
- - NonceManager valida nonce (nao repetido, counter valido)
1126
+ - Extracts ciphertext and nonce from the payload
1127
+ - NonceManager checks the nonce (not repeated, counter valid)
1035
1128
  - crypto_box_open_easy_afternm(ciphertext, nonce, sharedKeyAB)
1036
- - Se MAC falhar -> rejeita (mensagem corrompida/adulterada)
1037
- - Se MAC ok -> parse do JSON interno
1129
+ - MAC fails -> reject (corrupted or tampered with)
1130
+ - MAC passes -> parse the inner JSON
1038
1131
 
1039
- t7 ChatController recebe { text: "Ola", sentAt: t2, messageId: "a1b2" }
1040
- Valida que sentAt e razoavel (nao muito no passado/futuro)
1132
+ t7 ChatController receives { text: "Hi", sentAt: t2, messageId: "a1b2" }
1133
+ Checks sentAt is reasonable (not far in the past or future)
1041
1134
 
1042
- t8 UI.displayMessage("Alice", "Ola", timestamp)
1043
- Bob ve: [10:30] Alice: Ola
1135
+ t8 UI.addMessage("Alice", "Hi")
1136
+ Bob sees the message under an "10:30 🦊 Alice" header
1044
1137
  ```
1045
1138
 
1046
1139
  ### 8.2 Scenario: Group Chat (broadcast)
@@ -1082,45 +1175,47 @@ a server change and is not wired up there yet.)
1082
1175
  ### 9.1 Server
1083
1176
 
1084
1177
  ```
1085
- 1. Carregar constantes (constants.js)
1086
- 2. Criar instancia WebSocketServer na porta configurada
1087
- 3. Criar SessionManager (mapa vazio de sessoes)
1088
- 4. Criar MessageRouter (referencia ao SessionManager)
1089
- 5. Registrar handlers:
1178
+ 1. Load configuration and constants (config.js, constants.js)
1179
+ 2. Run the preflight checks (port free, certs readable, limits sane)
1180
+ 3. Load or generate the TLS certificate (CertManager) wss:// by default
1181
+ 4. Create the WebSocketServer on the configured port
1182
+ 5. Create the SessionManager (empty session map) and the ConnectionGuard
1183
+ 6. Create the MessageRouter (holding a reference to the SessionManager)
1184
+ 7. Register handlers:
1090
1185
  - on('connection') -> SessionManager.handleConnection()
1091
1186
  - on('close') -> SessionManager.handleDisconnection()
1092
1187
  - on('message') -> MessageRouter.route()
1093
- 6. Iniciar heartbeat interval (ping todos os clientes a cada 30s)
1094
- 7. Registrar SIGINT/SIGTERM para graceful shutdown:
1095
- - Notificar todos os clientes
1096
- - Fechar conexoes
1097
- - Limpar recursos
1098
- 8. Imprimir no console:
1099
- - IP local (todas as interfaces de rede)
1100
- - Porta
1101
- - "Servidor pronto. Clientes podem conectar em ws://<IP>:3600"
1188
+ 8. Start the heartbeat interval (ping every client every 30s)
1189
+ 9. Register SIGINT/SIGTERM for a graceful shutdown:
1190
+ - Notify every client
1191
+ - Close the connections
1192
+ - Release resources
1193
+ 10. Print the banner: every local IP, the port, and the wss:// URLs
1102
1194
  ```
1103
1195
 
1104
1196
  ### 9.2 Client
1105
1197
 
1106
1198
  ```
1107
- 1. Exibir banner "SecureLAN Chat v1.0"
1108
- 2. Pedir nickname (validar: 1-20 chars, alfanumerico + underscore)
1109
- 3. Pedir endereco do servidor (default: localhost:3600)
1110
- 4. Gerar par de chaves (KeyManager)
1111
- 5. Exibir fingerprint da chave publica
1112
- 6. Conectar ao servidor via WebSocket
1113
- 7. Enviar mensagem JOIN (nickname + publicKey)
1114
- 8. Aguardar JOIN_ACK
1115
- 9. Se erro (nickname duplicado) -> pedir outro nickname
1116
- 10. Receber lista de peers e derivar sharedKey com cada um
1117
- 11. Inicializar UI blessed
1118
- 12. Exibir lista de usuarios online
1119
- 13. Entrar no loop de input
1120
- 14. Registrar handler de SIGINT para:
1121
- - sodium_memzero() em todas as chaves
1122
- - Fechar conexao WebSocket
1123
- - Destruir UI blessed
1199
+ 1. Show the CipherMesh banner
1200
+ 2. First run only: the setup wizard (nickname, server, theme), saved to the
1201
+ config file so it never asks twice
1202
+ 3. Ask for the nickname (1-20 chars, alphanumeric + underscore) unless saved
1203
+ 4. Ask for the server address, or accept an invite string (default: localhost:3600)
1204
+ 5. Restore the encrypted session state if there is one (StateManager)
1205
+ 6. Generate or load the key pair (KeyManager) and the device identity
1206
+ 7. Show the public-key fingerprint and its key art
1207
+ 8. Connect over WebSocket, pinning the certificate (CertPinStore)
1208
+ 9. Send JOIN (nickname + public key); wait for JOIN_ACK
1209
+ 10. On error (duplicate nickname) -> ask for another one
1210
+ 11. Receive the peer list and run the handshake with each, deriving a shared key
1211
+ 12. Start the blessed UI: request the keyboard protocols, draw the header,
1212
+ the chat log, the status bar and the composer
1213
+ 13. Enter the input loop
1214
+ 14. Register a SIGINT handler to:
1215
+ - sodium_memzero() every key
1216
+ - Persist the encrypted state
1217
+ - Close the WebSocket
1218
+ - Restore the terminal (keyboard protocols, bracketed paste) and destroy the UI
1124
1219
  ```
1125
1220
 
1126
1221
  ---
@@ -1268,7 +1363,7 @@ routing). With sealed sender, the *sender* side of the social graph stays hidden
1268
1363
  ```
1269
1364
  Startup:
1270
1365
  1. Se existe estado salvo → prompt passphrase → loadState() → restaura KeyManager, Handshake, peers
1271
- 2. Se nao existeprompt passphrase opcional (para proteger sessao futura)
1366
+ 2. If there is none optional passphrase prompt (to protect a future session)
1272
1367
 
1273
1368
  Shutdown (Ctrl+C, /quit):
1274
1369
  Se passphrase definida → serializeState() → saveState() cifrado
@@ -1316,48 +1411,49 @@ Shutdown (Ctrl+C, /quit):
1316
1411
  **Future evolution** — P2P with a DHT (for larger networks):
1317
1412
  ```
1318
1413
  1. Distributed Hash Table para discovery
1319
- 2. Cada no mantem tabela de roteamento parcial
1320
- 3. Mensagens podem ser roteadas por multiplos hops
1321
- 4. Redundancia e tolerancia a falhas
1414
+ 2. Each node keeps a partial routing table
1415
+ 3. Messages can be routed over multiple hops
1416
+ 4. Redundancy and fault tolerance
1322
1417
  ```
1323
1418
 
1324
- ### 11.6 Professional Open-Source Project
1419
+ ### 11.6 Project Infrastructure
1420
+
1421
+ Most of this section used to be a wishlist. It is now a status list, which is a
1422
+ better thing for it to be — what is left is the short part.
1423
+
1424
+ **In place**:
1325
1425
 
1326
- **Repository structure**:
1327
1426
  ```
1328
- securelan-chat/
1329
- ├── .github/
1330
- │ ├── workflows/
1331
- ├── ci.yml # CI: lint + test em cada PR
1332
- ├── release.yml # Release automatica com tags
1333
- │ └── security-audit.yml # npm audit semanal
1334
- │ ├── ISSUE_TEMPLATE/
1335
- ├── bug_report.md
1336
- └── feature_request.md
1337
- ├── PULL_REQUEST_TEMPLATE.md
1338
- └── CODEOWNERS
1339
- ├── docs/
1340
- │ ├── ARCHITECTURE.md
1341
- │ ├── SECURITY.md # Politica de seguranca
1342
- │ ├── CONTRIBUTING.md # Guia de contribuicao
1343
- │ └── PROTOCOL.md # Especificacao do protocolo
1344
- ├── LICENSE # MIT ou Apache-2.0
1345
- ├── CHANGELOG.md # Historico de mudancas (semver)
1346
- ├── CODE_OF_CONDUCT.md
1347
- └── SECURITY.md # Como reportar vulnerabilidades
1427
+ .github/
1428
+ ├── workflows/
1429
+ │ ├── ci.yml # lint + format + tests, Node 20 and 22, on every PR
1430
+ │ ├── codeql.yml # CodeQL static analysis
1431
+ │ ├── release.yml # validate -> npm publish (OIDC) -> GitHub Release -> relay deploy
1432
+ ├── binaries.yml # standalone binaries
1433
+ │ ├── docker-publish.yml # image to GHCR
1434
+ │ ├── deploy.yml # site deploy, called by docker-publish
1435
+ │ └── hub-monitor.yml # scheduled hub health checks
1436
+ ├── dependabot.yml
1437
+ └── CODEOWNERS
1348
1438
  ```
1349
1439
 
1350
- **Best practices**:
1351
- - Semantic versioning (semver)
1352
- - Conventional commits
1353
- - CI/CD with GitHub Actions
1354
- - Dependabot to update dependencies
1355
- - CodeQL for static security analysis
1356
- - Releases signed with GPG
1357
- - Documentation on GitHub Pages
1358
- - Badges in the README (CI, coverage, license, version)
1359
- - Issue templates and PR templates
1360
- - Security policy with a responsible-disclosure process
1440
+ - Semantic versioning, and conventional commits enforced by commitlint
1441
+ - `LICENSE` (MIT), `SECURITY.md` with a disclosure process, `CONTRIBUTING.md`,
1442
+ `TERMS.md`
1443
+ - `CHANGELOG.md`, an entry per release
1444
+ - Badges in the README
1445
+ - npm publishing over **OIDC Trusted Publishing** — no long-lived token exists to
1446
+ leak
1447
+ - A Homebrew formula (`Formula/ciphermesh.rb`), whose digest is filled in after
1448
+ the tag publishes the tarball
1449
+
1450
+ **Not done, and worth doing**:
1451
+
1452
+ - Issue and pull-request templates
1453
+ - A code of conduct
1454
+ - GPG-signed release artefacts (the npm provenance attestation covers part of
1455
+ this, but not the GitHub Release assets)
1456
+ - Published API documentation
1361
1457
 
1362
1458
  ### 11.7 Other Improvements
1363
1459