ciphermesh 1.0.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 (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +251 -0
  3. package/README.pt-BR.md +253 -0
  4. package/bin/ciphermesh.js +34 -0
  5. package/docs/ARCHITECTURE.md +1188 -0
  6. package/docs/SETUP.md +305 -0
  7. package/docs/demo.svg +46 -0
  8. package/package.json +87 -0
  9. package/src/client/ChatController.js +2476 -0
  10. package/src/client/Connection.js +129 -0
  11. package/src/client/FileTransfer.js +488 -0
  12. package/src/client/ImagePreview.js +88 -0
  13. package/src/client/UI.js +1830 -0
  14. package/src/client/index.js +231 -0
  15. package/src/crypto/CertPinStore.js +79 -0
  16. package/src/crypto/DeniableEncrypt.js +53 -0
  17. package/src/crypto/DoubleRatchet.js +574 -0
  18. package/src/crypto/Handshake.js +219 -0
  19. package/src/crypto/HistoryStore.js +241 -0
  20. package/src/crypto/IdentityBackup.js +70 -0
  21. package/src/crypto/KeyManager.js +134 -0
  22. package/src/crypto/MessageCrypto.js +181 -0
  23. package/src/crypto/NonceManager.js +72 -0
  24. package/src/crypto/SealedSender.js +58 -0
  25. package/src/crypto/SenderKey.js +204 -0
  26. package/src/crypto/StateManager.js +138 -0
  27. package/src/crypto/TrustStore.js +216 -0
  28. package/src/p2p/Discovery.js +80 -0
  29. package/src/p2p/P2PChatController.js +1856 -0
  30. package/src/p2p/PeerConnectionManager.js +252 -0
  31. package/src/p2p/PeerServer.js +68 -0
  32. package/src/p2p/index.js +219 -0
  33. package/src/protocol/messages.js +138 -0
  34. package/src/protocol/validators.js +175 -0
  35. package/src/server/CertManager.js +173 -0
  36. package/src/server/MessageRouter.js +80 -0
  37. package/src/server/OfflineQueue.js +124 -0
  38. package/src/server/SessionManager.js +296 -0
  39. package/src/server/WebSocketServer.js +632 -0
  40. package/src/server/index.js +89 -0
  41. package/src/shared/AuditLog.js +91 -0
  42. package/src/shared/PluginManager.js +83 -0
  43. package/src/shared/banner.js +271 -0
  44. package/src/shared/commandSuggest.js +59 -0
  45. package/src/shared/config.js +90 -0
  46. package/src/shared/constants.js +126 -0
  47. package/src/shared/coverTraffic.js +34 -0
  48. package/src/shared/dnd.js +60 -0
  49. package/src/shared/emoji.js +17 -0
  50. package/src/shared/fuzzy.js +40 -0
  51. package/src/shared/invite.js +61 -0
  52. package/src/shared/keyArt.js +66 -0
  53. package/src/shared/logger.js +38 -0
  54. package/src/shared/panic.js +38 -0
  55. package/src/shared/prompt.js +31 -0
  56. package/src/shared/terminalGraphics.js +72 -0
  57. package/src/shared/themes.js +36 -0
  58. package/src/shared/voiceNote.js +128 -0
@@ -0,0 +1,128 @@
1
+ import { spawn, execFileSync } from 'node:child_process';
2
+ import { platform } from 'node:os';
3
+ import { join } from 'node:path';
4
+
5
+ // Voice notes reuse the encrypted file-transfer path: record to a temp .wav,
6
+ // send it as a file, and the receiver can play it back. Recording/playback is
7
+ // delegated to whatever CLI audio tool is installed (sox / ffmpeg / afplay),
8
+ // so this module stays a thin, mostly-pure wrapper.
9
+
10
+ const AUDIO_RE = /\.(wav|opus|mp3|m4a|ogg|flac|aac)$/i;
11
+
12
+ /** True if a filename looks like an audio note. */
13
+ export function isAudioFile(name) {
14
+ return AUDIO_RE.test(name || '');
15
+ }
16
+
17
+ // A path controlled upstream (a peer picks the received filename) must never be
18
+ // read as a CLI flag by the audio tool. These tools don't all honour a `--`
19
+ // separator, so we force flag-looking paths to be treated as files by prefixing
20
+ // `./` — universally safe and tool-agnostic against argv flag smuggling.
21
+ export function guardPath(p) {
22
+ return typeof p === 'string' && p.startsWith('-') ? `./${p}` : p;
23
+ }
24
+
25
+ /** Build the record command for a detected tool. Pure — exported for testing. */
26
+ export function recordCommand(tool, outPath, seconds, os = platform()) {
27
+ const dur = String(Math.max(1, Math.round(seconds)));
28
+ const out = guardPath(outPath);
29
+ switch (tool) {
30
+ case 'rec': // sox's record frontend
31
+ return { cmd: 'rec', args: ['-q', out, 'trim', '0', dur] };
32
+ case 'sox':
33
+ return { cmd: 'sox', args: ['-q', '-d', '-t', 'wav', out, 'trim', '0', dur] };
34
+ case 'ffmpeg':
35
+ return {
36
+ cmd: 'ffmpeg',
37
+ args: [
38
+ '-y',
39
+ '-f',
40
+ os === 'darwin' ? 'avfoundation' : 'alsa',
41
+ '-i',
42
+ os === 'darwin' ? ':0' : 'default',
43
+ '-t',
44
+ dur,
45
+ out,
46
+ ],
47
+ };
48
+ default:
49
+ return null;
50
+ }
51
+ }
52
+
53
+ /** Build the playback command for a detected tool. Pure — exported for testing. */
54
+ export function playCommand(tool, path) {
55
+ const p = guardPath(path);
56
+ switch (tool) {
57
+ case 'afplay': // macOS, built-in
58
+ return { cmd: 'afplay', args: [p] };
59
+ case 'play': // sox
60
+ return { cmd: 'play', args: ['-q', p] };
61
+ case 'ffplay':
62
+ return { cmd: 'ffplay', args: ['-nodisp', '-autoexit', '-loglevel', 'quiet', p] };
63
+ default:
64
+ return null;
65
+ }
66
+ }
67
+
68
+ function commandExists(cmd) {
69
+ try {
70
+ execFileSync(platform() === 'win32' ? 'where' : 'which', [cmd], { stdio: 'ignore' });
71
+ return true;
72
+ } catch {
73
+ return false;
74
+ }
75
+ }
76
+
77
+ /** First available recording tool, or null. */
78
+ export function detectRecorder() {
79
+ for (const tool of ['rec', 'sox', 'ffmpeg']) {
80
+ if (commandExists(tool)) {
81
+ return tool;
82
+ }
83
+ }
84
+ return null;
85
+ }
86
+
87
+ /** First available playback tool, or null (afplay ships with macOS). */
88
+ export function detectPlayer() {
89
+ const candidates = platform() === 'darwin' ? ['afplay', 'play', 'ffplay'] : ['play', 'ffplay'];
90
+ for (const tool of candidates) {
91
+ if (commandExists(tool)) {
92
+ return tool;
93
+ }
94
+ }
95
+ return null;
96
+ }
97
+
98
+ /**
99
+ * Record `seconds` of audio into `dir`, resolving with the file path.
100
+ * The tool auto-stops after the duration (sox `trim` / ffmpeg `-t`).
101
+ */
102
+ export function recordVoiceNote(dir, seconds, now) {
103
+ const tool = detectRecorder();
104
+ if (!tool) {
105
+ return Promise.reject(new Error('no recorder found — install sox (e.g. brew install sox)'));
106
+ }
107
+ const out = join(dir, `voice-${now}.wav`);
108
+ const spec = recordCommand(tool, out, seconds);
109
+ return new Promise((resolve, reject) => {
110
+ const proc = spawn(spec.cmd, spec.args, { stdio: 'ignore' });
111
+ proc.on('error', reject);
112
+ proc.on('exit', (code) => (code === 0 ? resolve(out) : reject(new Error('recording failed'))));
113
+ });
114
+ }
115
+
116
+ /** Play an audio file, resolving when playback finishes. */
117
+ export function playVoiceNote(path) {
118
+ const tool = detectPlayer();
119
+ if (!tool) {
120
+ return Promise.reject(new Error('no audio player found (install sox or use macOS)'));
121
+ }
122
+ const spec = playCommand(tool, path);
123
+ return new Promise((resolve, reject) => {
124
+ const proc = spawn(spec.cmd, spec.args, { stdio: 'ignore' });
125
+ proc.on('error', reject);
126
+ proc.on('exit', () => resolve());
127
+ });
128
+ }