@meshwhisper/node 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.
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +547 -0
- package/dist/index.js.map +1 -0
- package/package.json +28 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// ============================================================
|
|
3
|
+
// MeshWhisper Node
|
|
4
|
+
// Bundles four functions into one process on one port:
|
|
5
|
+
// 1. Relay — WebSocket relay + store-and-forward
|
|
6
|
+
// 2. Directory — Prekey bundle lookup (X3DH cold-start)
|
|
7
|
+
// 3. Push — APNs/FCM wake signals via webhook
|
|
8
|
+
// 4. Media — Encrypted blob storage (TTL-based, opaque)
|
|
9
|
+
//
|
|
10
|
+
// Usage:
|
|
11
|
+
// PORT=443 node dist/index.js
|
|
12
|
+
// PORT=8080 node dist/index.js (dev)
|
|
13
|
+
//
|
|
14
|
+
// Push webhook:
|
|
15
|
+
// PUSH_WEBHOOK_URL=https://your-push-server/notify
|
|
16
|
+
//
|
|
17
|
+
// When a blob arrives for an offline recipient that has a registered push
|
|
18
|
+
// token, the Node POSTs JSON to PUSH_WEBHOOK_URL:
|
|
19
|
+
// { token, platform, topic?, destHash }
|
|
20
|
+
//
|
|
21
|
+
// The webhook is responsible for sending the actual APNs/FCM notification.
|
|
22
|
+
// The Node only sends a silent wake signal — no message content is included.
|
|
23
|
+
//
|
|
24
|
+
// Self-hosted: one Docker container, one VPS, everything included.
|
|
25
|
+
// Foundation-hosted: same binary, run by the Foundation as public infra.
|
|
26
|
+
// ============================================================
|
|
27
|
+
import * as http from 'node:http';
|
|
28
|
+
import * as https from 'node:https';
|
|
29
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
30
|
+
// ============================================================
|
|
31
|
+
// Configuration
|
|
32
|
+
// ============================================================
|
|
33
|
+
const PORT = parseInt(process.env.PORT ?? '8080', 10);
|
|
34
|
+
const BLOB_TTL_HOURS = parseInt(process.env.BLOB_TTL_HOURS ?? '72', 10);
|
|
35
|
+
const MAX_BLOB_SIZE = parseInt(process.env.MAX_BLOB_SIZE ?? String(256 * 1024), 10); // 256 KB
|
|
36
|
+
const MAX_BLOBS_PER_HASH = parseInt(process.env.MAX_BLOBS_PER_HASH ?? '500', 10);
|
|
37
|
+
const MEDIA_TTL_HOURS = parseInt(process.env.MEDIA_TTL_HOURS ?? String(7 * 24), 10); // 7 days
|
|
38
|
+
const MAX_MEDIA_SIZE = parseInt(process.env.MAX_MEDIA_SIZE ?? String(50 * 1024 * 1024), 10); // 50 MB
|
|
39
|
+
const PRUNE_INTERVAL_MS = 5 * 60 * 1000; // prune expired blobs every 5 minutes
|
|
40
|
+
const PUSH_WEBHOOK_URL = process.env.PUSH_WEBHOOK_URL ?? null;
|
|
41
|
+
// Rate limiting (per IP, sliding window)
|
|
42
|
+
const RATE_WINDOW_MS = 60_000; // 1 minute window
|
|
43
|
+
const RATE_LIMIT_MEDIA = parseInt(process.env.RATE_LIMIT_MEDIA ?? '20', 10); // uploads/min
|
|
44
|
+
const RATE_LIMIT_DIR = parseInt(process.env.RATE_LIMIT_DIR ?? '60', 10); // registrations/min
|
|
45
|
+
/** Canonical external base URL for constructing media download links.
|
|
46
|
+
* Set this when the Node is behind a reverse proxy (nginx, Caddy, Cloudflare).
|
|
47
|
+
* Example: BASE_URL=https://msg.myapp.com
|
|
48
|
+
* If unset, the URL is inferred from the Host header (works for local dev). */
|
|
49
|
+
const BASE_URL = (process.env.BASE_URL ?? '').replace(/\/$/, '');
|
|
50
|
+
const rateLimitState = new Map();
|
|
51
|
+
/** Returns true if the request is within limits, false if it should be rejected. */
|
|
52
|
+
function checkRateLimit(ip, bucket, maxPerWindow) {
|
|
53
|
+
const key = `${bucket}:${ip}`;
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const entry = rateLimitState.get(key);
|
|
56
|
+
if (!entry || now - entry.windowStart >= RATE_WINDOW_MS) {
|
|
57
|
+
rateLimitState.set(key, { count: 1, windowStart: now });
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
if (entry.count >= maxPerWindow)
|
|
61
|
+
return false;
|
|
62
|
+
entry.count++;
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
function getClientIp(req) {
|
|
66
|
+
// Respect X-Forwarded-For when behind a proxy
|
|
67
|
+
const forwarded = req.headers['x-forwarded-for'];
|
|
68
|
+
if (typeof forwarded === 'string')
|
|
69
|
+
return forwarded.split(',')[0].trim();
|
|
70
|
+
return req.socket.remoteAddress ?? 'unknown';
|
|
71
|
+
}
|
|
72
|
+
// Prune stale rate limit entries alongside blobs
|
|
73
|
+
function pruneRateLimitState() {
|
|
74
|
+
const cutoff = Date.now() - RATE_WINDOW_MS;
|
|
75
|
+
for (const [key, entry] of rateLimitState.entries()) {
|
|
76
|
+
if (entry.windowStart < cutoff)
|
|
77
|
+
rateLimitState.delete(key);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// ============================================================
|
|
81
|
+
// Packet header constants (must match SDK wire format)
|
|
82
|
+
//
|
|
83
|
+
// Binary layout (all big-endian):
|
|
84
|
+
// [0] version u8
|
|
85
|
+
// [1] flags u8
|
|
86
|
+
// [2..9] destHash 8 bytes
|
|
87
|
+
// [10..25] senderEphId 16 bytes
|
|
88
|
+
// [26] ttl u8
|
|
89
|
+
// [27..30] payloadLen u32
|
|
90
|
+
// [31..] encrypted payload
|
|
91
|
+
// ============================================================
|
|
92
|
+
const HEADER_SIZE = 31;
|
|
93
|
+
const DEST_HASH_OFFSET = 2;
|
|
94
|
+
const DEST_HASH_LENGTH = 8;
|
|
95
|
+
function readDestHash(buf) {
|
|
96
|
+
if (buf.byteLength < HEADER_SIZE)
|
|
97
|
+
return null;
|
|
98
|
+
const bytes = buf.subarray(DEST_HASH_OFFSET, DEST_HASH_OFFSET + DEST_HASH_LENGTH);
|
|
99
|
+
return Buffer.from(bytes).toString('hex');
|
|
100
|
+
}
|
|
101
|
+
/** Map from destHash (hex) → list of queued blobs. */
|
|
102
|
+
const blobStore = new Map();
|
|
103
|
+
function storeBlob(destHash, data) {
|
|
104
|
+
let queue = blobStore.get(destHash);
|
|
105
|
+
if (!queue) {
|
|
106
|
+
queue = [];
|
|
107
|
+
blobStore.set(destHash, queue);
|
|
108
|
+
}
|
|
109
|
+
if (queue.length >= MAX_BLOBS_PER_HASH) {
|
|
110
|
+
queue.shift(); // drop oldest when full
|
|
111
|
+
}
|
|
112
|
+
queue.push({ data, receivedAt: Date.now() });
|
|
113
|
+
}
|
|
114
|
+
function pullBlobs(destHash) {
|
|
115
|
+
const queue = blobStore.get(destHash);
|
|
116
|
+
if (!queue || queue.length === 0)
|
|
117
|
+
return [];
|
|
118
|
+
blobStore.delete(destHash);
|
|
119
|
+
return queue.map((b) => b.data);
|
|
120
|
+
}
|
|
121
|
+
function pruneExpiredBlobs() {
|
|
122
|
+
const cutoff = Date.now() - BLOB_TTL_HOURS * 60 * 60 * 1000;
|
|
123
|
+
for (const [hash, queue] of blobStore.entries()) {
|
|
124
|
+
const fresh = queue.filter((b) => b.receivedAt > cutoff);
|
|
125
|
+
if (fresh.length === 0) {
|
|
126
|
+
blobStore.delete(hash);
|
|
127
|
+
// Also expire the push token for this dest hash — if no blobs have
|
|
128
|
+
// arrived within the blob TTL window, the device has likely rotated
|
|
129
|
+
// its dest hashes and the token is stale.
|
|
130
|
+
pushTokens.delete(hash);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
blobStore.set(hash, fresh);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** Map from destHash (hex) → push registration for offline wake signals. */
|
|
138
|
+
const pushTokens = new Map();
|
|
139
|
+
function registerPushTokens(destHashes, reg) {
|
|
140
|
+
for (const hash of destHashes) {
|
|
141
|
+
pushTokens.set(hash, reg);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function deregisterPushTokens(destHashes) {
|
|
145
|
+
for (const hash of destHashes) {
|
|
146
|
+
pushTokens.delete(hash);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Fire-and-forget POST to the configured push webhook.
|
|
151
|
+
* Payload: { token, platform, topic?, destHash }
|
|
152
|
+
* No retry — best-effort. If there is no webhook configured, this is a no-op.
|
|
153
|
+
*/
|
|
154
|
+
function notifyPush(destHash, reg) {
|
|
155
|
+
if (!PUSH_WEBHOOK_URL)
|
|
156
|
+
return;
|
|
157
|
+
const body = JSON.stringify({
|
|
158
|
+
token: reg.token,
|
|
159
|
+
platform: reg.platform,
|
|
160
|
+
...(reg.topic ? { topic: reg.topic } : {}),
|
|
161
|
+
...(reg.pushSubscription ? { pushSubscription: JSON.parse(reg.pushSubscription) } : {}),
|
|
162
|
+
destHash,
|
|
163
|
+
});
|
|
164
|
+
try {
|
|
165
|
+
const url = new URL(PUSH_WEBHOOK_URL);
|
|
166
|
+
const transport = url.protocol === 'https:' ? https : http;
|
|
167
|
+
const req = transport.request(url, {
|
|
168
|
+
method: 'POST',
|
|
169
|
+
headers: {
|
|
170
|
+
'Content-Type': 'application/json',
|
|
171
|
+
'Content-Length': Buffer.byteLength(body),
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
req.on('error', () => { });
|
|
175
|
+
req.write(body);
|
|
176
|
+
req.end();
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Misconfigured URL or network error — swallow
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// ============================================================
|
|
183
|
+
// Prekey directory — in-memory, namespace-scoped
|
|
184
|
+
// ============================================================
|
|
185
|
+
/** Map from `${namespace}:${publicKeyHex}` → serialized bundle (base64). */
|
|
186
|
+
const prekeyDirectory = new Map();
|
|
187
|
+
function directoryKey(namespace, publicKey) {
|
|
188
|
+
return `${namespace}:${publicKey}`;
|
|
189
|
+
}
|
|
190
|
+
function registerPrekey(namespace, publicKey, bundle) {
|
|
191
|
+
prekeyDirectory.set(directoryKey(namespace, publicKey), bundle);
|
|
192
|
+
}
|
|
193
|
+
function lookupPrekey(namespace, publicKey) {
|
|
194
|
+
return prekeyDirectory.get(directoryKey(namespace, publicKey)) ?? null;
|
|
195
|
+
}
|
|
196
|
+
/** Map from random media ID (hex) → encrypted media blob. */
|
|
197
|
+
const mediaStore = new Map();
|
|
198
|
+
function generateMediaId() {
|
|
199
|
+
const bytes = new Uint8Array(16);
|
|
200
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
201
|
+
return Buffer.from(bytes).toString('hex');
|
|
202
|
+
}
|
|
203
|
+
function storeMedia(data) {
|
|
204
|
+
const id = generateMediaId();
|
|
205
|
+
const storedAt = Date.now();
|
|
206
|
+
mediaStore.set(id, { data, storedAt });
|
|
207
|
+
return { id, expiresAt: storedAt + MEDIA_TTL_HOURS * 60 * 60 * 1000 };
|
|
208
|
+
}
|
|
209
|
+
function fetchMedia(id) {
|
|
210
|
+
const entry = mediaStore.get(id);
|
|
211
|
+
if (!entry)
|
|
212
|
+
return null;
|
|
213
|
+
const expiresAt = entry.storedAt + MEDIA_TTL_HOURS * 60 * 60 * 1000;
|
|
214
|
+
if (Date.now() > expiresAt) {
|
|
215
|
+
mediaStore.delete(id);
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
return entry.data;
|
|
219
|
+
}
|
|
220
|
+
function pruneExpiredMedia() {
|
|
221
|
+
const cutoff = Date.now() - MEDIA_TTL_HOURS * 60 * 60 * 1000;
|
|
222
|
+
for (const [id, entry] of mediaStore.entries()) {
|
|
223
|
+
if (entry.storedAt < cutoff)
|
|
224
|
+
mediaStore.delete(id);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// ============================================================
|
|
228
|
+
// Connected clients — map from destHash (hex) → WebSocket
|
|
229
|
+
// ============================================================
|
|
230
|
+
/** A client may register multiple dest hashes (current + previous epoch). */
|
|
231
|
+
const clientsByHash = new Map();
|
|
232
|
+
/** Reverse map so we can clean up on disconnect. */
|
|
233
|
+
const hashesPerClient = new Map();
|
|
234
|
+
function registerClient(ws, destHashes) {
|
|
235
|
+
// Remove any previous registrations for this socket
|
|
236
|
+
const existing = hashesPerClient.get(ws);
|
|
237
|
+
if (existing) {
|
|
238
|
+
for (const h of existing)
|
|
239
|
+
clientsByHash.delete(h);
|
|
240
|
+
}
|
|
241
|
+
const hashes = new Set();
|
|
242
|
+
for (const h of destHashes) {
|
|
243
|
+
clientsByHash.set(h, ws);
|
|
244
|
+
hashes.add(h);
|
|
245
|
+
}
|
|
246
|
+
hashesPerClient.set(ws, hashes);
|
|
247
|
+
}
|
|
248
|
+
function deregisterClient(ws) {
|
|
249
|
+
const hashes = hashesPerClient.get(ws);
|
|
250
|
+
if (hashes) {
|
|
251
|
+
for (const h of hashes)
|
|
252
|
+
clientsByHash.delete(h);
|
|
253
|
+
// Push tokens are intentionally kept after disconnect — they are needed
|
|
254
|
+
// to wake the device when it is offline. They expire naturally alongside
|
|
255
|
+
// the blobs in the prune cycle.
|
|
256
|
+
hashesPerClient.delete(ws);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function deliverQueuedBlobs(ws, destHashes) {
|
|
260
|
+
for (const hash of destHashes) {
|
|
261
|
+
const blobs = pullBlobs(hash);
|
|
262
|
+
for (const blob of blobs) {
|
|
263
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
264
|
+
ws.send(blob, { binary: true });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// ============================================================
|
|
270
|
+
// Relay — incoming binary packet handler
|
|
271
|
+
// ============================================================
|
|
272
|
+
function handleRelayPacket(data, sender) {
|
|
273
|
+
if (data.byteLength > MAX_BLOB_SIZE)
|
|
274
|
+
return; // drop oversized packets
|
|
275
|
+
const destHash = readDestHash(data);
|
|
276
|
+
if (!destHash)
|
|
277
|
+
return; // malformed header
|
|
278
|
+
const recipient = clientsByHash.get(destHash);
|
|
279
|
+
if (recipient && recipient !== sender && recipient.readyState === WebSocket.OPEN) {
|
|
280
|
+
// Recipient is connected — forward directly
|
|
281
|
+
recipient.send(data, { binary: true });
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
// Recipient offline (or same socket) — store for later delivery
|
|
285
|
+
storeBlob(destHash, data);
|
|
286
|
+
// Wake the recipient via push if they have a registered token
|
|
287
|
+
const pushReg = pushTokens.get(destHash);
|
|
288
|
+
if (pushReg)
|
|
289
|
+
notifyPush(destHash, pushReg);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// ============================================================
|
|
293
|
+
// WebSocket server
|
|
294
|
+
// ============================================================
|
|
295
|
+
function handleWebSocketConnection(ws) {
|
|
296
|
+
ws.on('message', (raw) => {
|
|
297
|
+
if (raw instanceof ArrayBuffer) {
|
|
298
|
+
handleRelayPacket(new Uint8Array(raw), ws);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (Buffer.isBuffer(raw)) {
|
|
302
|
+
handleRelayPacket(new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength), ws);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
// JSON control message
|
|
306
|
+
try {
|
|
307
|
+
const msg = JSON.parse(raw.toString());
|
|
308
|
+
if (msg.type === 'hello' && Array.isArray(msg.destHashes)) {
|
|
309
|
+
const hashes = msg.destHashes.filter((h) => typeof h === 'string' && /^[0-9a-f]{16}$/.test(h));
|
|
310
|
+
registerClient(ws, hashes);
|
|
311
|
+
// Register push token/subscription if provided
|
|
312
|
+
if (msg.pushPlatform === 'webpush' && typeof msg.pushSubscription === 'string') {
|
|
313
|
+
const reg = {
|
|
314
|
+
token: msg.pushSubscription, // subscription JSON stored in token field
|
|
315
|
+
platform: 'webpush',
|
|
316
|
+
pushSubscription: msg.pushSubscription,
|
|
317
|
+
};
|
|
318
|
+
registerPushTokens(hashes, reg);
|
|
319
|
+
}
|
|
320
|
+
else if (typeof msg.pushToken === 'string' && msg.pushToken &&
|
|
321
|
+
(msg.pushPlatform === 'apns' || msg.pushPlatform === 'fcm')) {
|
|
322
|
+
const reg = {
|
|
323
|
+
token: msg.pushToken,
|
|
324
|
+
platform: msg.pushPlatform,
|
|
325
|
+
...(typeof msg.pushTopic === 'string' && msg.pushTopic ? { topic: msg.pushTopic } : {}),
|
|
326
|
+
};
|
|
327
|
+
registerPushTokens(hashes, reg);
|
|
328
|
+
}
|
|
329
|
+
deliverQueuedBlobs(ws, hashes);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (msg.type === 'pull') {
|
|
333
|
+
const hashes = hashesPerClient.get(ws);
|
|
334
|
+
if (hashes)
|
|
335
|
+
deliverQueuedBlobs(ws, [...hashes]);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
// Not JSON or malformed — ignore
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
ws.on('close', () => {
|
|
344
|
+
deregisterClient(ws);
|
|
345
|
+
});
|
|
346
|
+
ws.on('error', () => {
|
|
347
|
+
deregisterClient(ws);
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
// ============================================================
|
|
351
|
+
// HTTP handler — health check and prekey directory
|
|
352
|
+
// ============================================================
|
|
353
|
+
function parseBody(req) {
|
|
354
|
+
return new Promise((resolve, reject) => {
|
|
355
|
+
const chunks = [];
|
|
356
|
+
req.on('data', (chunk) => chunks.push(chunk));
|
|
357
|
+
req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
|
|
358
|
+
req.on('error', reject);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
function sendJson(res, status, body) {
|
|
362
|
+
const payload = JSON.stringify(body);
|
|
363
|
+
res.writeHead(status, {
|
|
364
|
+
'Content-Type': 'application/json',
|
|
365
|
+
'Content-Length': Buffer.byteLength(payload),
|
|
366
|
+
'Access-Control-Allow-Origin': '*',
|
|
367
|
+
});
|
|
368
|
+
res.end(payload);
|
|
369
|
+
}
|
|
370
|
+
async function handleHttp(req, res) {
|
|
371
|
+
const url = new URL(req.url ?? '/', `http://localhost`);
|
|
372
|
+
const method = req.method ?? 'GET';
|
|
373
|
+
// CORS preflight
|
|
374
|
+
if (method === 'OPTIONS') {
|
|
375
|
+
res.writeHead(204, {
|
|
376
|
+
'Access-Control-Allow-Origin': '*',
|
|
377
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
378
|
+
'Access-Control-Allow-Headers': 'Content-Type',
|
|
379
|
+
});
|
|
380
|
+
res.end();
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
// Health check
|
|
384
|
+
if (url.pathname === '/health' && method === 'GET') {
|
|
385
|
+
sendJson(res, 200, {
|
|
386
|
+
status: 'ok',
|
|
387
|
+
clients: clientsByHash.size,
|
|
388
|
+
storedBlobs: [...blobStore.values()].reduce((n, q) => n + q.length, 0),
|
|
389
|
+
prekeyEntries: prekeyDirectory.size,
|
|
390
|
+
pushRegistrations: pushTokens.size,
|
|
391
|
+
mediaEntries: mediaStore.size,
|
|
392
|
+
});
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
// Register prekey bundle
|
|
396
|
+
// POST /directory { namespace, publicKey, bundle }
|
|
397
|
+
if (url.pathname === '/directory' && method === 'POST') {
|
|
398
|
+
if (!checkRateLimit(getClientIp(req), 'dir', RATE_LIMIT_DIR)) {
|
|
399
|
+
sendJson(res, 429, { error: 'Too many requests' });
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
let body;
|
|
403
|
+
try {
|
|
404
|
+
body = JSON.parse(await parseBody(req));
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
sendJson(res, 400, { error: 'Invalid JSON body' });
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
const { namespace, publicKey, bundle } = body;
|
|
411
|
+
if (typeof namespace !== 'string' || !namespace ||
|
|
412
|
+
typeof publicKey !== 'string' || !publicKey ||
|
|
413
|
+
typeof bundle !== 'string' || !bundle) {
|
|
414
|
+
sendJson(res, 400, { error: 'Missing required fields: namespace, publicKey, bundle' });
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
registerPrekey(namespace, publicKey, bundle);
|
|
418
|
+
sendJson(res, 200, { ok: true });
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
// Lookup prekey bundle
|
|
422
|
+
// GET /directory?namespace=&publicKey=
|
|
423
|
+
if (url.pathname === '/directory' && method === 'GET') {
|
|
424
|
+
const namespace = url.searchParams.get('namespace');
|
|
425
|
+
const publicKey = url.searchParams.get('publicKey');
|
|
426
|
+
if (!namespace || !publicKey) {
|
|
427
|
+
sendJson(res, 400, { error: 'Missing query params: namespace, publicKey' });
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
const bundle = lookupPrekey(namespace, publicKey);
|
|
431
|
+
if (!bundle) {
|
|
432
|
+
sendJson(res, 404, { error: 'Prekey bundle not found' });
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
sendJson(res, 200, { bundle });
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
// Upload encrypted media blob
|
|
439
|
+
// POST /media (binary body, Content-Length required)
|
|
440
|
+
// Returns: { id, url, expiresAt }
|
|
441
|
+
if (url.pathname === '/media' && method === 'POST') {
|
|
442
|
+
if (!checkRateLimit(getClientIp(req), 'media', RATE_LIMIT_MEDIA)) {
|
|
443
|
+
sendJson(res, 429, { error: 'Too many requests' });
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
const contentLength = parseInt(req.headers['content-length'] ?? '0', 10);
|
|
447
|
+
if (contentLength > MAX_MEDIA_SIZE) {
|
|
448
|
+
sendJson(res, 413, { error: `Payload too large (max ${MAX_MEDIA_SIZE} bytes)` });
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const chunks = [];
|
|
452
|
+
let received = 0;
|
|
453
|
+
let aborted = false;
|
|
454
|
+
await new Promise((resolve, reject) => {
|
|
455
|
+
req.on('data', (chunk) => {
|
|
456
|
+
received += chunk.length;
|
|
457
|
+
if (received > MAX_MEDIA_SIZE) {
|
|
458
|
+
aborted = true;
|
|
459
|
+
req.destroy();
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
chunks.push(chunk);
|
|
463
|
+
});
|
|
464
|
+
req.on('end', resolve);
|
|
465
|
+
req.on('error', reject);
|
|
466
|
+
});
|
|
467
|
+
if (aborted) {
|
|
468
|
+
sendJson(res, 413, { error: `Payload too large (max ${MAX_MEDIA_SIZE} bytes)` });
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
const data = Buffer.concat(chunks);
|
|
472
|
+
const { id, expiresAt } = storeMedia(data);
|
|
473
|
+
const base = BASE_URL || `http://${req.headers['host'] ?? `localhost:${PORT}`}`;
|
|
474
|
+
const mediaUrl = `${base}/media/${id}`;
|
|
475
|
+
sendJson(res, 200, { id, url: mediaUrl, expiresAt });
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
// Download encrypted media blob
|
|
479
|
+
// GET /media/:id
|
|
480
|
+
const mediaMatch = url.pathname.match(/^\/media\/([0-9a-f]{32})$/);
|
|
481
|
+
if (mediaMatch && method === 'GET') {
|
|
482
|
+
const data = fetchMedia(mediaMatch[1]);
|
|
483
|
+
if (!data) {
|
|
484
|
+
sendJson(res, 404, { error: 'Media not found or expired' });
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
res.writeHead(200, {
|
|
488
|
+
'Content-Type': 'application/octet-stream',
|
|
489
|
+
'Content-Length': data.length,
|
|
490
|
+
'Access-Control-Allow-Origin': '*',
|
|
491
|
+
'Cache-Control': 'private, max-age=604800', // 7 days
|
|
492
|
+
});
|
|
493
|
+
res.end(data);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
sendJson(res, 404, { error: 'Not found' });
|
|
497
|
+
}
|
|
498
|
+
// ============================================================
|
|
499
|
+
// Server startup
|
|
500
|
+
// ============================================================
|
|
501
|
+
const httpServer = http.createServer((req, res) => {
|
|
502
|
+
handleHttp(req, res).catch((err) => {
|
|
503
|
+
console.error('[ERROR] HTTP handler threw:', err);
|
|
504
|
+
res.writeHead(500).end();
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
508
|
+
wss.on('connection', (ws) => {
|
|
509
|
+
handleWebSocketConnection(ws);
|
|
510
|
+
});
|
|
511
|
+
httpServer.on('upgrade', (req, socket, head) => {
|
|
512
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
513
|
+
wss.emit('connection', ws, req);
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
// Prune expired blobs and media on a regular interval
|
|
517
|
+
const pruneInterval = setInterval(() => {
|
|
518
|
+
pruneExpiredBlobs();
|
|
519
|
+
pruneExpiredMedia();
|
|
520
|
+
pruneRateLimitState();
|
|
521
|
+
}, PRUNE_INTERVAL_MS);
|
|
522
|
+
pruneInterval.unref(); // don't keep process alive for pruning alone
|
|
523
|
+
httpServer.listen(PORT, () => {
|
|
524
|
+
console.log(`MeshWhisper Node listening on port ${PORT}`);
|
|
525
|
+
console.log(` Relay: ws://localhost:${PORT}`);
|
|
526
|
+
console.log(` Directory: http://localhost:${PORT}/directory`);
|
|
527
|
+
console.log(` Media: http://localhost:${PORT}/media`);
|
|
528
|
+
console.log(` Health: http://localhost:${PORT}/health`);
|
|
529
|
+
console.log(` Blob TTL: ${BLOB_TTL_HOURS}h`);
|
|
530
|
+
console.log(` Media TTL: ${MEDIA_TTL_HOURS}h (max ${MAX_MEDIA_SIZE / (1024 * 1024)}MB per file)`);
|
|
531
|
+
console.log(` Base URL: ${BASE_URL || '(inferred from Host header — set BASE_URL in production)'}`);
|
|
532
|
+
console.log(` Push: ${PUSH_WEBHOOK_URL ?? 'disabled (set PUSH_WEBHOOK_URL to enable)'}`);
|
|
533
|
+
});
|
|
534
|
+
// ============================================================
|
|
535
|
+
// Graceful shutdown
|
|
536
|
+
// ============================================================
|
|
537
|
+
function shutdown() {
|
|
538
|
+
console.log('\nShutting down...');
|
|
539
|
+
clearInterval(pruneInterval);
|
|
540
|
+
wss.clients.forEach((ws) => ws.close(1001, 'Node shutting down'));
|
|
541
|
+
wss.close();
|
|
542
|
+
httpServer.close(() => process.exit(0));
|
|
543
|
+
setTimeout(() => process.exit(1), 3000).unref();
|
|
544
|
+
}
|
|
545
|
+
process.on('SIGINT', shutdown);
|
|
546
|
+
process.on('SIGTERM', shutdown);
|
|
547
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@meshwhisper/node",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MeshWhisper Node — relay, push forwarding, media storage, and prekey directory in a single binary",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"meshwhisper-node": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": ["dist"],
|
|
11
|
+
"publishConfig": { "access": "public" },
|
|
12
|
+
"scripts": {
|
|
13
|
+
"prepublishOnly": "npm run build",
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"dev": "tsx src/index.ts"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"ws": "^8.18.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^22.0.0",
|
|
23
|
+
"@types/ws": "^8.5.0",
|
|
24
|
+
"tsx": "^4.21.0",
|
|
25
|
+
"typescript": "^5.7.0"
|
|
26
|
+
},
|
|
27
|
+
"license": "MIT"
|
|
28
|
+
}
|