@meshwhisper/sdk 0.1.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 (163) hide show
  1. package/README.md +138 -0
  2. package/dist/browser/index.d.ts +4 -0
  3. package/dist/browser/index.d.ts.map +1 -0
  4. package/dist/browser/index.js +19 -0
  5. package/dist/browser/index.js.map +1 -0
  6. package/dist/chaff/index.d.ts +91 -0
  7. package/dist/chaff/index.d.ts.map +1 -0
  8. package/dist/chaff/index.js +268 -0
  9. package/dist/chaff/index.js.map +1 -0
  10. package/dist/cluster/index.d.ts +159 -0
  11. package/dist/cluster/index.d.ts.map +1 -0
  12. package/dist/cluster/index.js +393 -0
  13. package/dist/cluster/index.js.map +1 -0
  14. package/dist/compliance/index.d.ts +129 -0
  15. package/dist/compliance/index.d.ts.map +1 -0
  16. package/dist/compliance/index.js +315 -0
  17. package/dist/compliance/index.js.map +1 -0
  18. package/dist/crypto/index.d.ts +65 -0
  19. package/dist/crypto/index.d.ts.map +1 -0
  20. package/dist/crypto/index.js +146 -0
  21. package/dist/crypto/index.js.map +1 -0
  22. package/dist/group/index.d.ts +155 -0
  23. package/dist/group/index.d.ts.map +1 -0
  24. package/dist/group/index.js +560 -0
  25. package/dist/group/index.js.map +1 -0
  26. package/dist/index.d.ts +7 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +11 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/namespace/index.d.ts +155 -0
  31. package/dist/namespace/index.d.ts.map +1 -0
  32. package/dist/namespace/index.js +278 -0
  33. package/dist/namespace/index.js.map +1 -0
  34. package/dist/node/index.d.ts +4 -0
  35. package/dist/node/index.d.ts.map +1 -0
  36. package/dist/node/index.js +19 -0
  37. package/dist/node/index.js.map +1 -0
  38. package/dist/packet/index.d.ts +63 -0
  39. package/dist/packet/index.d.ts.map +1 -0
  40. package/dist/packet/index.js +244 -0
  41. package/dist/packet/index.js.map +1 -0
  42. package/dist/permissions/index.d.ts +107 -0
  43. package/dist/permissions/index.d.ts.map +1 -0
  44. package/dist/permissions/index.js +282 -0
  45. package/dist/permissions/index.js.map +1 -0
  46. package/dist/persistence/idb-storage.d.ts +27 -0
  47. package/dist/persistence/idb-storage.d.ts.map +1 -0
  48. package/dist/persistence/idb-storage.js +75 -0
  49. package/dist/persistence/idb-storage.js.map +1 -0
  50. package/dist/persistence/index.d.ts +4 -0
  51. package/dist/persistence/index.d.ts.map +1 -0
  52. package/dist/persistence/index.js +3 -0
  53. package/dist/persistence/index.js.map +1 -0
  54. package/dist/persistence/node-storage.d.ts +33 -0
  55. package/dist/persistence/node-storage.d.ts.map +1 -0
  56. package/dist/persistence/node-storage.js +90 -0
  57. package/dist/persistence/node-storage.js.map +1 -0
  58. package/dist/persistence/serialization.d.ts +4 -0
  59. package/dist/persistence/serialization.d.ts.map +1 -0
  60. package/dist/persistence/serialization.js +49 -0
  61. package/dist/persistence/serialization.js.map +1 -0
  62. package/dist/persistence/types.d.ts +29 -0
  63. package/dist/persistence/types.d.ts.map +1 -0
  64. package/dist/persistence/types.js +5 -0
  65. package/dist/persistence/types.js.map +1 -0
  66. package/dist/ratchet/index.d.ts +80 -0
  67. package/dist/ratchet/index.d.ts.map +1 -0
  68. package/dist/ratchet/index.js +259 -0
  69. package/dist/ratchet/index.js.map +1 -0
  70. package/dist/reciprocity/index.d.ts +109 -0
  71. package/dist/reciprocity/index.d.ts.map +1 -0
  72. package/dist/reciprocity/index.js +311 -0
  73. package/dist/reciprocity/index.js.map +1 -0
  74. package/dist/relay/index.d.ts +87 -0
  75. package/dist/relay/index.d.ts.map +1 -0
  76. package/dist/relay/index.js +286 -0
  77. package/dist/relay/index.js.map +1 -0
  78. package/dist/routing/index.d.ts +136 -0
  79. package/dist/routing/index.d.ts.map +1 -0
  80. package/dist/routing/index.js +478 -0
  81. package/dist/routing/index.js.map +1 -0
  82. package/dist/sdk/index.d.ts +322 -0
  83. package/dist/sdk/index.d.ts.map +1 -0
  84. package/dist/sdk/index.js +1530 -0
  85. package/dist/sdk/index.js.map +1 -0
  86. package/dist/sybil/index.d.ts +123 -0
  87. package/dist/sybil/index.d.ts.map +1 -0
  88. package/dist/sybil/index.js +491 -0
  89. package/dist/sybil/index.js.map +1 -0
  90. package/dist/transport/browser/index.d.ts +34 -0
  91. package/dist/transport/browser/index.d.ts.map +1 -0
  92. package/dist/transport/browser/index.js +176 -0
  93. package/dist/transport/browser/index.js.map +1 -0
  94. package/dist/transport/local/index.d.ts +57 -0
  95. package/dist/transport/local/index.d.ts.map +1 -0
  96. package/dist/transport/local/index.js +442 -0
  97. package/dist/transport/local/index.js.map +1 -0
  98. package/dist/transport/negotiator/index.d.ts +79 -0
  99. package/dist/transport/negotiator/index.d.ts.map +1 -0
  100. package/dist/transport/negotiator/index.js +289 -0
  101. package/dist/transport/negotiator/index.js.map +1 -0
  102. package/dist/transport/node/index.d.ts +56 -0
  103. package/dist/transport/node/index.d.ts.map +1 -0
  104. package/dist/transport/node/index.js +209 -0
  105. package/dist/transport/node/index.js.map +1 -0
  106. package/dist/transport/noop/index.d.ts +11 -0
  107. package/dist/transport/noop/index.d.ts.map +1 -0
  108. package/dist/transport/noop/index.js +20 -0
  109. package/dist/transport/noop/index.js.map +1 -0
  110. package/dist/transport/p2p/index.d.ts +109 -0
  111. package/dist/transport/p2p/index.d.ts.map +1 -0
  112. package/dist/transport/p2p/index.js +237 -0
  113. package/dist/transport/p2p/index.js.map +1 -0
  114. package/dist/transport/websocket/index.d.ts +89 -0
  115. package/dist/transport/websocket/index.d.ts.map +1 -0
  116. package/dist/transport/websocket/index.js +498 -0
  117. package/dist/transport/websocket/index.js.map +1 -0
  118. package/dist/transport/websocket/serialize.d.ts +5 -0
  119. package/dist/transport/websocket/serialize.d.ts.map +1 -0
  120. package/dist/transport/websocket/serialize.js +55 -0
  121. package/dist/transport/websocket/serialize.js.map +1 -0
  122. package/dist/types.d.ts +215 -0
  123. package/dist/types.d.ts.map +1 -0
  124. package/dist/types.js +15 -0
  125. package/dist/types.js.map +1 -0
  126. package/dist/x3dh/index.d.ts +120 -0
  127. package/dist/x3dh/index.d.ts.map +1 -0
  128. package/dist/x3dh/index.js +290 -0
  129. package/dist/x3dh/index.js.map +1 -0
  130. package/package.json +59 -0
  131. package/src/browser/index.ts +19 -0
  132. package/src/chaff/index.ts +340 -0
  133. package/src/cluster/index.ts +482 -0
  134. package/src/compliance/index.ts +407 -0
  135. package/src/crypto/index.ts +193 -0
  136. package/src/group/index.ts +719 -0
  137. package/src/index.ts +87 -0
  138. package/src/lz4js.d.ts +58 -0
  139. package/src/namespace/index.ts +336 -0
  140. package/src/node/index.ts +19 -0
  141. package/src/packet/index.ts +326 -0
  142. package/src/permissions/index.ts +405 -0
  143. package/src/persistence/idb-storage.ts +83 -0
  144. package/src/persistence/index.ts +3 -0
  145. package/src/persistence/node-storage.ts +96 -0
  146. package/src/persistence/serialization.ts +75 -0
  147. package/src/persistence/types.ts +33 -0
  148. package/src/ratchet/index.ts +363 -0
  149. package/src/reciprocity/index.ts +371 -0
  150. package/src/relay/index.ts +382 -0
  151. package/src/routing/index.ts +577 -0
  152. package/src/sdk/index.ts +1994 -0
  153. package/src/sybil/index.ts +661 -0
  154. package/src/transport/browser/index.ts +201 -0
  155. package/src/transport/local/index.ts +540 -0
  156. package/src/transport/negotiator/index.ts +397 -0
  157. package/src/transport/node/index.ts +234 -0
  158. package/src/transport/noop/index.ts +22 -0
  159. package/src/transport/p2p/index.ts +345 -0
  160. package/src/transport/websocket/index.ts +660 -0
  161. package/src/transport/websocket/serialize.ts +68 -0
  162. package/src/types.ts +275 -0
  163. package/src/x3dh/index.ts +388 -0
package/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # MeshWhisper
2
+
3
+ Serverless P2P end-to-end encrypted messaging SDK. Drop messaging into any app — PWA, React Native, Node.js — without building or operating a message server that can read your users' messages.
4
+
5
+ ## How it works
6
+
7
+ ```
8
+ Alice's device Node (relay) Bob's device
9
+ ────────────── ──────────── ────────────
10
+ MeshWhisper.init() ──ws──► stores blob ◄──ws── MeshWhisper.init()
11
+ send(bobId, msg) ──────► routes by destHash ──────► onMessage(msg)
12
+ (cannot decrypt)
13
+ ```
14
+
15
+ - **X3DH** key exchange on first contact — no prior communication needed
16
+ - **Double Ratchet** per message — forward secrecy, break-in recovery
17
+ - **Destination hash routing** — the Node knows *where* to route, never *who* sent what
18
+ - **Store and forward** — messages queue on the Node while the recipient is offline
19
+ - **Push wake** — Node triggers a silent APNs/FCM/Web Push when a message arrives for an offline device
20
+
21
+ The Node relay and push service are the only infrastructure you run. They are intentionally dumb: they relay encrypted bytes and ring a doorbell. They cannot read messages.
22
+
23
+ ---
24
+
25
+ ## Repository layout
26
+
27
+ ```
28
+ @meshwhisper/sdk — client SDK (browser + Node.js)
29
+ src/sdk/index.ts — public API surface
30
+ src/transport/browser/ — BrowserTransport (native WebSocket)
31
+ src/transport/node/ — NodeTransport (ws package)
32
+ src/persistence/idb-* — IndexedDB storage backend
33
+ src/persistence/node-* — Filesystem storage backend
34
+
35
+ @meshwhisper/node (node/) — relay server (WebSocket + HTTP)
36
+ @meshwhisper/push-service — APNs / FCM / Web Push dispatcher
37
+ @meshwhisper/cli (cli/) — npx @meshwhisper/cli init
38
+ @meshwhisper/service-worker — PWA service worker helper
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Quick start — PWA
44
+
45
+ ```ts
46
+ import { MeshWhisper } from '@meshwhisper/sdk';
47
+
48
+ // Auto-detects browser: uses IDBStorage + BrowserTransport
49
+ const mw = await MeshWhisper.init({
50
+ namespace: 'com.example.myapp',
51
+ node: 'wss://relay.myapp.com', // your self-hosted Node, or 'mesh'
52
+ onMessage: (message) => {
53
+ const text = new TextDecoder().decode(new Uint8Array(message.payload));
54
+ appendToChat(message.senderId, text);
55
+ },
56
+ });
57
+
58
+ const myId = mw.getLocalPeerId(); // share this with contacts
59
+
60
+ // First message to a new contact initiates X3DH automatically
61
+ await MeshWhisper.send(contactId, new TextEncoder().encode('Hello!'));
62
+ ```
63
+
64
+ ## Quick start — Node.js
65
+
66
+ ```ts
67
+ import { MeshWhisper } from '@meshwhisper/sdk/node';
68
+ import { NodeStorage } from '@meshwhisper/sdk/node';
69
+
70
+ const mw = await MeshWhisper.init({
71
+ namespace: 'com.example.myapp',
72
+ node: 'wss://relay.myapp.com',
73
+ storage: new NodeStorage('./data'), // persists identity + sessions to disk
74
+ onMessage: (message) => {
75
+ const text = new TextDecoder().decode(new Uint8Array(message.payload));
76
+ console.log(`[${message.senderId}]: ${text}`);
77
+ },
78
+ });
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Self-hosting in 5 minutes
84
+
85
+ See **[docs/self-hosting.md](docs/self-hosting.md)** for the full guide.
86
+
87
+ The short version — copy this `docker-compose.yml` and fill in the push credentials you need:
88
+
89
+ ```yaml
90
+ services:
91
+ node:
92
+ image: ghcr.io/meshwhisper/node:latest # or build from node/Dockerfile
93
+ ports: ["8080:8080"]
94
+ environment:
95
+ BASE_URL: "https://relay.myapp.com"
96
+ PUSH_WEBHOOK_URL: "http://push:4000/notify"
97
+
98
+ push:
99
+ image: ghcr.io/meshwhisper/push-service:latest
100
+ environment:
101
+ VAPID_PUBLIC_KEY: "..."
102
+ VAPID_PRIVATE_KEY: "..."
103
+ VAPID_SUBJECT: "mailto:ops@myapp.com"
104
+ # APNS_KEY_ID / APNS_TEAM_ID / APNS_KEY_PATH / APNS_BUNDLE_ID (iOS)
105
+ # FCM_SERVICE_ACCOUNT_PATH / FCM_PROJECT_ID (Android)
106
+ ```
107
+
108
+ ```bash
109
+ docker compose up -d
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Scaffolding a new project
115
+
116
+ ```bash
117
+ npx @meshwhisper/cli init
118
+ ```
119
+
120
+ Prompts for your bundle ID and node URL, then prints your `.env` block, SDK init snippet, and (optionally) a `docker-compose.yml`.
121
+
122
+ ---
123
+
124
+ ## Full API reference
125
+
126
+ See **[docs/api.md](docs/api.md)**.
127
+
128
+ ---
129
+
130
+ ## Security model
131
+
132
+ - The relay Node sees only encrypted ciphertext and truncated destination hashes. It cannot link a message to a sender identity.
133
+ - Destination hashes rotate every hour, limiting traffic-analysis correlation windows.
134
+ - The push service receives a token/subscription and a destination hash — no message content.
135
+ - Identity keys are generated on-device and never leave the device. The private key is stored in the configured `StorageBackend` only.
136
+ - Media blobs are encrypted locally before upload. The Node stores ciphertext; the decryption key is sent through the ratchet-encrypted message channel, never via the Node's HTTP API.
137
+
138
+ For a detailed threat model see the PRD at `meshwhisper-prd-v1.2.md`.
@@ -0,0 +1,4 @@
1
+ export * from '../index.js';
2
+ export { IDBStorage } from '../persistence/idb-storage.js';
3
+ export { BrowserTransport } from '../transport/browser/index.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAgBA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,19 @@
1
+ // ============================================================
2
+ // MeshWhisper SDK — Browser / PWA entry point
3
+ // @meshwhisper/sdk/browser
4
+ //
5
+ // Re-exports everything from the main SDK plus browser-specific
6
+ // storage and transport implementations.
7
+ //
8
+ // Usage:
9
+ // import { MeshWhisper, IDBStorage, BrowserTransport } from '@meshwhisper/sdk/browser';
10
+ //
11
+ // When you call MeshWhisper.init() in a browser environment, the SDK
12
+ // detects window/indexedDB and auto-selects IDBStorage + BrowserTransport.
13
+ // You only need to import from this path if you want to reference the
14
+ // classes explicitly (e.g. to construct IDBStorage with a custom namespace).
15
+ // ============================================================
16
+ export * from '../index.js';
17
+ export { IDBStorage } from '../persistence/idb-storage.js';
18
+ export { BrowserTransport } from '../transport/browser/index.js';
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,8CAA8C;AAC9C,2BAA2B;AAC3B,EAAE;AACF,gEAAgE;AAChE,yCAAyC;AACzC,EAAE;AACF,SAAS;AACT,0FAA0F;AAC1F,EAAE;AACF,qEAAqE;AACrE,2EAA2E;AAC3E,sEAAsE;AACtE,6EAA6E;AAC7E,+DAA+D;AAE/D,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,91 @@
1
+ import { Packet, ChaffRate, RelayWillingness } from '../types.js';
2
+ export interface ChaffOptions {
3
+ /** Emission rate preset. */
4
+ rate: ChaffRate;
5
+ /** Maximum chaff payload size in bytes. Defaults to 256. */
6
+ maxPacketSize?: number;
7
+ /** Minimum chaff payload size in bytes. Defaults to 32. */
8
+ minPacketSize?: number;
9
+ /** Randomness applied to inter-packet timing (0.0-1.0). Defaults to 0.3. */
10
+ burstVariance?: number;
11
+ }
12
+ export interface ChaffStats {
13
+ packetsGenerated: number;
14
+ bytesGenerated: number;
15
+ /** Elapsed time since start() was first called, in milliseconds. */
16
+ uptime: number;
17
+ currentRate: ChaffRate;
18
+ }
19
+ export declare class ChaffGenerator {
20
+ private readonly minPacketSize;
21
+ private readonly maxPacketSize;
22
+ private readonly burstVariance;
23
+ private rate;
24
+ private running;
25
+ private timer;
26
+ private startedAt;
27
+ private packetsGenerated;
28
+ private bytesGenerated;
29
+ private emitCallback;
30
+ constructor(options?: ChaffOptions);
31
+ /**
32
+ * Generate a single chaff packet.
33
+ *
34
+ * The packet is constructed to be byte-for-byte indistinguishable from
35
+ * a real encrypted message to any external observer: all variable-length
36
+ * fields are filled with cryptographically random data, the flags field
37
+ * is set to CHAFF (which the local node recognises but a relay treats
38
+ * identically to DATA), and the TTL is kept low so chaff doesn't
39
+ * propagate far.
40
+ */
41
+ generateChaffPacket(): Packet;
42
+ /** Begin emitting chaff on a jittered schedule. */
43
+ start(): void;
44
+ /** Stop chaff emission. */
45
+ stop(): void;
46
+ /** Whether the generator is currently emitting. */
47
+ isRunning(): boolean;
48
+ /** Change the emission rate. Takes effect on the next scheduling cycle. */
49
+ setRate(rate: ChaffRate): void;
50
+ /**
51
+ * Automatically adjust the chaff rate based on the device's relay
52
+ * willingness setting.
53
+ *
54
+ * - eager → high
55
+ * - willing → normal
56
+ * - reluctant → low
57
+ * - unavailable → stop entirely
58
+ */
59
+ adaptToRelayWillingness(willingness: RelayWillingness): void;
60
+ /**
61
+ * Surround a real packet with 0-2 chaff packets whose payload sizes
62
+ * approximate the real packet's size, making it harder for an observer
63
+ * to distinguish the real message in a burst.
64
+ *
65
+ * Returns an array of 1-3 packets with the real packet placed at a
66
+ * random position.
67
+ */
68
+ camouflageRealMessage(realPacket: Packet): Packet[];
69
+ /**
70
+ * Register a callback that receives each generated chaff packet.
71
+ * The transport layer will call this to enqueue chaff for sending.
72
+ */
73
+ onChaffGenerated(callback: (packet: Packet) => void): void;
74
+ /** Return runtime statistics for monitoring / diagnostics. */
75
+ getStats(): ChaffStats;
76
+ /**
77
+ * Build a chaff packet with the given payload size.
78
+ */
79
+ private buildChaffPacket;
80
+ /**
81
+ * Compute the next emission delay in milliseconds, adding jitter
82
+ * proportional to burstVariance so inter-packet timing is not
83
+ * predictable.
84
+ */
85
+ private computeNextDelay;
86
+ /**
87
+ * Schedule the next chaff emission.
88
+ */
89
+ private scheduleNext;
90
+ }
91
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/chaff/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,MAAM,EAAe,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AA2C/E,MAAM,WAAW,YAAY;IAC3B,4BAA4B;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAMD,MAAM,WAAW,UAAU;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,SAAS,CAAC;CACxB;AAiDD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC,OAAO,CAAC,IAAI,CAAY;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,SAAS,CAAuB;IAExC,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,cAAc,CAAK;IAE3B,OAAO,CAAC,YAAY,CAA2C;gBAEnD,OAAO,CAAC,EAAE,YAAY;IAWlC;;;;;;;;;OASG;IACH,mBAAmB,IAAI,MAAM;IAS7B,mDAAmD;IACnD,KAAK,IAAI,IAAI;IASb,2BAA2B;IAC3B,IAAI,IAAI,IAAI;IAQZ,mDAAmD;IACnD,SAAS,IAAI,OAAO;IAQpB,2EAA2E;IAC3E,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAI9B;;;;;;;;OAQG;IACH,uBAAuB,CAAC,WAAW,EAAE,gBAAgB,GAAG,IAAI;IAe5D;;;;;;;OAOG;IACH,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IA0BnD;;;OAGG;IACH,gBAAgB,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAQ1D,8DAA8D;IAC9D,QAAQ,IAAI,UAAU;IAatB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAkBxB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAQxB;;OAEG;IACH,OAAO,CAAC,YAAY;CAoBrB"}
@@ -0,0 +1,268 @@
1
+ // ============================================================
2
+ // MeshWhisper SDK — Chaff Generator & Traffic Analysis Defense
3
+ // Emits a constant stream of encrypted chaff packets that are
4
+ // byte-for-byte indistinguishable from real encrypted messages,
5
+ // defeating traffic analysis as described in PRD section 8.4.
6
+ // ============================================================
7
+ import { PacketFlags } from '../types.js';
8
+ // ---------------------------------------------------------------------------
9
+ // Constants
10
+ // ---------------------------------------------------------------------------
11
+ /** Destination hash length in bytes (truncated BLAKE3). */
12
+ const DEST_HASH_LENGTH = 8;
13
+ /** Sender ephemeral ID length in bytes. */
14
+ const SENDER_EPHEMERAL_ID_LENGTH = 16;
15
+ /** Packet version used across the SDK. */
16
+ const PACKET_VERSION = 1;
17
+ /** Default minimum chaff payload size in bytes. */
18
+ const DEFAULT_MIN_PACKET_SIZE = 32;
19
+ /** Default maximum chaff payload size in bytes. */
20
+ const DEFAULT_MAX_PACKET_SIZE = 256;
21
+ /** Default burst variance (0.0-1.0). */
22
+ const DEFAULT_BURST_VARIANCE = 0.3;
23
+ /** Base emission intervals per rate, in milliseconds. */
24
+ const RATE_INTERVALS = {
25
+ low: 60_000, // ~1 packet / 60 s → ~1 KB/h
26
+ normal: 30_000, // ~1 packet / 30 s → ~2 KB/h
27
+ high: 10_000, // ~1 packet / 10 s → ~6 KB/h
28
+ };
29
+ /** Maps relay willingness to the appropriate chaff rate. */
30
+ const WILLINGNESS_TO_RATE = {
31
+ eager: 'high',
32
+ willing: 'normal',
33
+ reluctant: 'low',
34
+ unavailable: null,
35
+ };
36
+ // ---------------------------------------------------------------------------
37
+ // Crypto helpers (isomorphic: works in Node.js and browsers)
38
+ // ---------------------------------------------------------------------------
39
+ /**
40
+ * Fill a Uint8Array with cryptographically secure random bytes.
41
+ * Uses globalThis.crypto (Web Crypto) when available, otherwise
42
+ * falls back to Node.js crypto module.
43
+ */
44
+ function secureRandomBytes(length) {
45
+ const buf = new Uint8Array(length);
46
+ if (typeof globalThis !== 'undefined' && globalThis.crypto?.getRandomValues) {
47
+ globalThis.crypto.getRandomValues(buf);
48
+ }
49
+ else {
50
+ // Node.js environments where globalThis.crypto may not exist (older Node)
51
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
52
+ const nodeCrypto = require('node:crypto');
53
+ nodeCrypto.randomFillSync(buf);
54
+ }
55
+ return buf;
56
+ }
57
+ /**
58
+ * Return a uniformly distributed random integer in [min, max] (inclusive).
59
+ */
60
+ function randomInt(min, max) {
61
+ const range = max - min + 1;
62
+ const bytes = secureRandomBytes(4);
63
+ const value = ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0;
64
+ return min + (value % range);
65
+ }
66
+ /**
67
+ * Return a random floating-point value in [0, 1).
68
+ */
69
+ function randomFloat() {
70
+ const bytes = secureRandomBytes(4);
71
+ const value = ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0;
72
+ return value / 0x1_0000_0000;
73
+ }
74
+ // ---------------------------------------------------------------------------
75
+ // ChaffGenerator
76
+ // ---------------------------------------------------------------------------
77
+ export class ChaffGenerator {
78
+ minPacketSize;
79
+ maxPacketSize;
80
+ burstVariance;
81
+ rate;
82
+ running = false;
83
+ timer = null;
84
+ startedAt = null;
85
+ packetsGenerated = 0;
86
+ bytesGenerated = 0;
87
+ emitCallback = null;
88
+ constructor(options) {
89
+ this.rate = options?.rate ?? 'normal';
90
+ this.minPacketSize = options?.minPacketSize ?? DEFAULT_MIN_PACKET_SIZE;
91
+ this.maxPacketSize = options?.maxPacketSize ?? DEFAULT_MAX_PACKET_SIZE;
92
+ this.burstVariance = Math.max(0, Math.min(1, options?.burstVariance ?? DEFAULT_BURST_VARIANCE));
93
+ }
94
+ // -----------------------------------------------------------------------
95
+ // Packet generation
96
+ // -----------------------------------------------------------------------
97
+ /**
98
+ * Generate a single chaff packet.
99
+ *
100
+ * The packet is constructed to be byte-for-byte indistinguishable from
101
+ * a real encrypted message to any external observer: all variable-length
102
+ * fields are filled with cryptographically random data, the flags field
103
+ * is set to CHAFF (which the local node recognises but a relay treats
104
+ * identically to DATA), and the TTL is kept low so chaff doesn't
105
+ * propagate far.
106
+ */
107
+ generateChaffPacket() {
108
+ const payloadSize = randomInt(this.minPacketSize, this.maxPacketSize);
109
+ return this.buildChaffPacket(payloadSize);
110
+ }
111
+ // -----------------------------------------------------------------------
112
+ // Emission scheduling
113
+ // -----------------------------------------------------------------------
114
+ /** Begin emitting chaff on a jittered schedule. */
115
+ start() {
116
+ if (this.running)
117
+ return;
118
+ this.running = true;
119
+ if (this.startedAt === null) {
120
+ this.startedAt = Date.now();
121
+ }
122
+ this.scheduleNext();
123
+ }
124
+ /** Stop chaff emission. */
125
+ stop() {
126
+ this.running = false;
127
+ if (this.timer !== null) {
128
+ clearTimeout(this.timer);
129
+ this.timer = null;
130
+ }
131
+ }
132
+ /** Whether the generator is currently emitting. */
133
+ isRunning() {
134
+ return this.running;
135
+ }
136
+ // -----------------------------------------------------------------------
137
+ // Rate control
138
+ // -----------------------------------------------------------------------
139
+ /** Change the emission rate. Takes effect on the next scheduling cycle. */
140
+ setRate(rate) {
141
+ this.rate = rate;
142
+ }
143
+ /**
144
+ * Automatically adjust the chaff rate based on the device's relay
145
+ * willingness setting.
146
+ *
147
+ * - eager → high
148
+ * - willing → normal
149
+ * - reluctant → low
150
+ * - unavailable → stop entirely
151
+ */
152
+ adaptToRelayWillingness(willingness) {
153
+ const mapped = WILLINGNESS_TO_RATE[willingness];
154
+ if (mapped === null) {
155
+ this.stop();
156
+ return;
157
+ }
158
+ this.rate = mapped;
159
+ // If already running, let the current timer expire naturally — the
160
+ // new rate will be picked up on the next scheduling cycle.
161
+ }
162
+ // -----------------------------------------------------------------------
163
+ // Real message camouflage
164
+ // -----------------------------------------------------------------------
165
+ /**
166
+ * Surround a real packet with 0-2 chaff packets whose payload sizes
167
+ * approximate the real packet's size, making it harder for an observer
168
+ * to distinguish the real message in a burst.
169
+ *
170
+ * Returns an array of 1-3 packets with the real packet placed at a
171
+ * random position.
172
+ */
173
+ camouflageRealMessage(realPacket) {
174
+ const chaffCount = randomInt(0, 2);
175
+ if (chaffCount === 0)
176
+ return [realPacket];
177
+ const realSize = realPacket.encryptedPayload.length;
178
+ // Build chaff with sizes close (±20%) to the real payload.
179
+ const sizeFloor = Math.max(1, Math.round(realSize * 0.8));
180
+ const sizeCeil = Math.round(realSize * 1.2);
181
+ const chaffPackets = [];
182
+ for (let i = 0; i < chaffCount; i++) {
183
+ const size = randomInt(sizeFloor, sizeCeil);
184
+ chaffPackets.push(this.buildChaffPacket(size));
185
+ }
186
+ // Insert the real packet at a random position in the burst.
187
+ const insertionIndex = randomInt(0, chaffPackets.length);
188
+ chaffPackets.splice(insertionIndex, 0, realPacket);
189
+ return chaffPackets;
190
+ }
191
+ // -----------------------------------------------------------------------
192
+ // Callback registration
193
+ // -----------------------------------------------------------------------
194
+ /**
195
+ * Register a callback that receives each generated chaff packet.
196
+ * The transport layer will call this to enqueue chaff for sending.
197
+ */
198
+ onChaffGenerated(callback) {
199
+ this.emitCallback = callback;
200
+ }
201
+ // -----------------------------------------------------------------------
202
+ // Statistics
203
+ // -----------------------------------------------------------------------
204
+ /** Return runtime statistics for monitoring / diagnostics. */
205
+ getStats() {
206
+ return {
207
+ packetsGenerated: this.packetsGenerated,
208
+ bytesGenerated: this.bytesGenerated,
209
+ uptime: this.startedAt !== null ? Date.now() - this.startedAt : 0,
210
+ currentRate: this.rate,
211
+ };
212
+ }
213
+ // -----------------------------------------------------------------------
214
+ // Private helpers
215
+ // -----------------------------------------------------------------------
216
+ /**
217
+ * Build a chaff packet with the given payload size.
218
+ */
219
+ buildChaffPacket(payloadSize) {
220
+ const encryptedPayload = secureRandomBytes(payloadSize);
221
+ const packet = {
222
+ version: PACKET_VERSION,
223
+ flags: PacketFlags.CHAFF,
224
+ destHash: secureRandomBytes(DEST_HASH_LENGTH),
225
+ senderEphemeralId: secureRandomBytes(SENDER_EPHEMERAL_ID_LENGTH),
226
+ ttl: randomInt(1, 3),
227
+ payloadLength: payloadSize,
228
+ encryptedPayload,
229
+ };
230
+ this.packetsGenerated++;
231
+ this.bytesGenerated += payloadSize;
232
+ return packet;
233
+ }
234
+ /**
235
+ * Compute the next emission delay in milliseconds, adding jitter
236
+ * proportional to burstVariance so inter-packet timing is not
237
+ * predictable.
238
+ */
239
+ computeNextDelay() {
240
+ const base = RATE_INTERVALS[this.rate];
241
+ const jitterRange = base * this.burstVariance;
242
+ // Uniform jitter in [-jitterRange, +jitterRange]
243
+ const jitter = (randomFloat() * 2 - 1) * jitterRange;
244
+ return Math.max(1, Math.round(base + jitter));
245
+ }
246
+ /**
247
+ * Schedule the next chaff emission.
248
+ */
249
+ scheduleNext() {
250
+ if (!this.running)
251
+ return;
252
+ const delay = this.computeNextDelay();
253
+ this.timer = setTimeout(() => {
254
+ if (!this.running)
255
+ return;
256
+ const packet = this.generateChaffPacket();
257
+ if (this.emitCallback) {
258
+ this.emitCallback(packet);
259
+ }
260
+ this.scheduleNext();
261
+ }, delay);
262
+ // Prevent the timer from keeping the process alive in Node.js.
263
+ if (typeof this.timer === 'object' && 'unref' in this.timer) {
264
+ this.timer.unref();
265
+ }
266
+ }
267
+ }
268
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/chaff/index.ts"],"names":[],"mappings":"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"}