forkoff 1.1.2 → 1.1.4
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/cloud-client.js +2 -1
- package/dist/crypto/e2eeManager.js +5 -13
- package/dist/server.js +2 -1
- package/dist/websocket.js +8 -8
- package/package.json +1 -1
package/dist/cloud-client.js
CHANGED
|
@@ -118,7 +118,8 @@ class CloudRelayClient extends events_1.EventEmitter {
|
|
|
118
118
|
});
|
|
119
119
|
// Handle mobile connected notification from relay
|
|
120
120
|
this.socket.on('mobile_connected', (data) => {
|
|
121
|
-
|
|
121
|
+
const id = data.deviceId || 'unknown';
|
|
122
|
+
console.log(`[CloudRelay] Mobile connected: ${id.length > 8 ? id.substring(0, 8) + '...' : id}`);
|
|
122
123
|
this.emit('mobile_connected', { deviceId: data.deviceId || data.mobileDeviceId });
|
|
123
124
|
});
|
|
124
125
|
// Handle mobile disconnected notification from relay
|
|
@@ -200,10 +200,7 @@ class E2EEManager {
|
|
|
200
200
|
const rawSharedKey = (0, keyExchange_1.computeSharedKey)(ephemeral.privateKey, init.ephemeralPublicKey);
|
|
201
201
|
// Derive directional send/receive keys via HKDF
|
|
202
202
|
const { sendKey, receiveKey } = (0, keyExchange_1.deriveSessionKeys)(rawSharedKey, this.deviceId, init.senderDeviceId);
|
|
203
|
-
|
|
204
|
-
const sendKeyFP = (0, tweetnacl_util_1.encodeBase64)(sendKey).substring(0, 12);
|
|
205
|
-
const recvKeyFP = (0, tweetnacl_util_1.encodeBase64)(receiveKey).substring(0, 12);
|
|
206
|
-
console.log(`[E2EE] Init: sendKey=${sendKeyFP}, recvKey=${recvKeyFP}, peer=${init.senderDeviceId}, myEphPub=${ephemeral.publicKey.substring(0, 12)}, peerEphPub=${init.ephemeralPublicKey.substring(0, 12)}`);
|
|
203
|
+
console.log(`[E2EE] Key exchange init processed — session established with peer ${init.senderDeviceId.substring(0, 8)}...`);
|
|
207
204
|
// Store session
|
|
208
205
|
const sessionId = `session-${init.senderDeviceId}-${this.deviceId}-${Date.now()}`;
|
|
209
206
|
this.sessions.set(init.senderDeviceId, {
|
|
@@ -249,7 +246,7 @@ class E2EEManager {
|
|
|
249
246
|
// If there's exactly one pending exchange, use it regardless of key.
|
|
250
247
|
if (!pendingEntry && this.pendingExchanges.size === 1) {
|
|
251
248
|
const [fallbackKey, fallbackEntry] = this.pendingExchanges.entries().next().value;
|
|
252
|
-
console.log(`[E2EE] Pending exchange not found for ${peerId
|
|
249
|
+
console.log(`[E2EE] Pending exchange not found for ${peerId.substring(0, 8)}..., using fallback`);
|
|
253
250
|
pendingEntry = fallbackEntry;
|
|
254
251
|
pendingKey = fallbackKey;
|
|
255
252
|
}
|
|
@@ -262,10 +259,7 @@ class E2EEManager {
|
|
|
262
259
|
const rawSharedKey = (0, keyExchange_1.computeSharedKey)(pending.privateKey, ack.ephemeralPublicKey);
|
|
263
260
|
// Derive directional send/receive keys via HKDF
|
|
264
261
|
const { sendKey, receiveKey } = (0, keyExchange_1.deriveSessionKeys)(rawSharedKey, this.deviceId, peerId);
|
|
265
|
-
|
|
266
|
-
const sendKeyFP = (0, tweetnacl_util_1.encodeBase64)(sendKey).substring(0, 12);
|
|
267
|
-
const recvKeyFP = (0, tweetnacl_util_1.encodeBase64)(receiveKey).substring(0, 12);
|
|
268
|
-
console.log(`[E2EE] Ack: sendKey=${sendKeyFP}, recvKey=${recvKeyFP}, peer=${peerId}, myEphPub=${pending.publicKey.substring(0, 12)}, peerEphPub=${ack.ephemeralPublicKey.substring(0, 12)}`);
|
|
262
|
+
console.log(`[E2EE] Key exchange ack processed — session established with peer ${peerId.substring(0, 8)}...`);
|
|
269
263
|
// Store session
|
|
270
264
|
const sessionId = `session-${this.deviceId}-${peerId}-${Date.now()}`;
|
|
271
265
|
this.sessions.set(peerId, {
|
|
@@ -338,10 +332,8 @@ class E2EEManager {
|
|
|
338
332
|
}
|
|
339
333
|
const payload = (0, encryption_1.encrypt)(plaintext, session.sendKey);
|
|
340
334
|
session.outgoingCounter++;
|
|
341
|
-
// Log encryption key fingerprint (first message only to avoid spam)
|
|
342
335
|
if (session.outgoingCounter === 1) {
|
|
343
|
-
|
|
344
|
-
console.log(`[E2EE] Encrypting first message with sendKey fingerprint=${keyFP}, recipient=${recipientDeviceId}`);
|
|
336
|
+
console.log(`[E2EE] First encrypted message sent to ${recipientDeviceId.substring(0, 8)}...`);
|
|
345
337
|
}
|
|
346
338
|
return {
|
|
347
339
|
senderDeviceId: this.deviceId,
|
|
@@ -374,7 +366,7 @@ class E2EEManager {
|
|
|
374
366
|
session.lastReceivedCounter = message.messageCounter;
|
|
375
367
|
// Check expiry AFTER decrypting — valid messages still get through, but warn caller
|
|
376
368
|
if (this.isSessionExpired(senderDeviceId)) {
|
|
377
|
-
console.warn(`[E2EE] Session with ${senderDeviceId} has expired
|
|
369
|
+
console.warn(`[E2EE] Session with ${senderDeviceId.substring(0, 8)}... has expired — re-key required`);
|
|
378
370
|
}
|
|
379
371
|
return plaintext;
|
|
380
372
|
}
|
package/dist/server.js
CHANGED
|
@@ -71,7 +71,8 @@ class EmbeddedRelayServer extends events_1.EventEmitter {
|
|
|
71
71
|
next();
|
|
72
72
|
});
|
|
73
73
|
this.io.on('connection', (socket) => {
|
|
74
|
-
|
|
74
|
+
const devId = socket.data.deviceId || 'unknown';
|
|
75
|
+
console.log(`[Server] Mobile connected: ${devId.length > 8 ? devId.substring(0, 8) + '...' : devId}`);
|
|
75
76
|
// Track the mobile socket (only one active connection)
|
|
76
77
|
if (this.mobileSocket) {
|
|
77
78
|
console.log(`[Server] Replacing existing mobile connection`);
|
package/dist/websocket.js
CHANGED
|
@@ -158,7 +158,7 @@ class WebSocketClient extends events_1.EventEmitter {
|
|
|
158
158
|
return;
|
|
159
159
|
// When mobile connects, emit connected + start heartbeat + initiate E2EE
|
|
160
160
|
this.server.on('mobile_connected', (data) => {
|
|
161
|
-
console.log(`[WS] Mobile connected: ${data.deviceId}
|
|
161
|
+
console.log(`[WS] Mobile connected: ${data.deviceId?.substring(0, 8)}...`);
|
|
162
162
|
this.emit('connected');
|
|
163
163
|
this.startHeartbeat();
|
|
164
164
|
// SECURITY: Clear stale E2EE peer state on every mobile connect.
|
|
@@ -167,7 +167,7 @@ class WebSocketClient extends events_1.EventEmitter {
|
|
|
167
167
|
// that mobile can't decrypt, causing "No session established" errors.
|
|
168
168
|
// A fresh key exchange will re-establish the session after debounce.
|
|
169
169
|
if (this.e2eePeerDeviceId) {
|
|
170
|
-
console.log(`[E2EE] Clearing stale peer state
|
|
170
|
+
console.log(`[E2EE] Clearing stale peer state (mobile reconnected)`);
|
|
171
171
|
this.e2eeManager?.clearSession(this.e2eePeerDeviceId);
|
|
172
172
|
this.e2eePeerDeviceId = null;
|
|
173
173
|
}
|
|
@@ -188,7 +188,7 @@ class WebSocketClient extends events_1.EventEmitter {
|
|
|
188
188
|
return;
|
|
189
189
|
try {
|
|
190
190
|
this._keyExchangePending = true;
|
|
191
|
-
console.log(`[E2EE] Initiating key exchange with ${targetId
|
|
191
|
+
console.log(`[E2EE] Initiating key exchange with ${targetId.substring(0, 8)}...`);
|
|
192
192
|
this.e2eeManager.clearSession(targetId);
|
|
193
193
|
const initPayload = this.e2eeManager.createKeyExchangeInit(targetId);
|
|
194
194
|
this.server?.emitToMobile('encrypted_key_exchange_init', {
|
|
@@ -249,7 +249,7 @@ class WebSocketClient extends events_1.EventEmitter {
|
|
|
249
249
|
return;
|
|
250
250
|
}
|
|
251
251
|
try {
|
|
252
|
-
console.log(`[E2EE] Received key exchange init from ${data.senderDeviceId}
|
|
252
|
+
console.log(`[E2EE] Received key exchange init from ${data.senderDeviceId.substring(0, 8)}...`);
|
|
253
253
|
const ack = this.e2eeManager.handleKeyExchangeInit(data);
|
|
254
254
|
this.e2eePeerDeviceId = data.senderDeviceId;
|
|
255
255
|
this.server?.emitToMobile('encrypted_key_exchange_ack', {
|
|
@@ -270,11 +270,11 @@ class WebSocketClient extends events_1.EventEmitter {
|
|
|
270
270
|
if (!this.e2eeManager)
|
|
271
271
|
return;
|
|
272
272
|
try {
|
|
273
|
-
console.log(`[E2EE] Received key exchange ack from ${data.senderDeviceId}
|
|
273
|
+
console.log(`[E2EE] Received key exchange ack from ${data.senderDeviceId.substring(0, 8)}...`);
|
|
274
274
|
this.e2eeManager.handleKeyExchangeAck(data);
|
|
275
275
|
this._keyExchangePending = false;
|
|
276
276
|
this.e2eePeerDeviceId = data.senderDeviceId;
|
|
277
|
-
console.log(`[E2EE] Key exchange complete — session established
|
|
277
|
+
console.log(`[E2EE] Key exchange complete — session established`);
|
|
278
278
|
this.emit('e2ee_established', { peerDeviceId: data.senderDeviceId });
|
|
279
279
|
this.flushSensitiveQueue();
|
|
280
280
|
this.sendAllUsageStats();
|
|
@@ -283,7 +283,7 @@ class WebSocketClient extends events_1.EventEmitter {
|
|
|
283
283
|
const msg = err instanceof Error ? err.message : String(err);
|
|
284
284
|
// Only suppress if it's truly a duplicate ack (pending exchange already consumed)
|
|
285
285
|
if (msg.includes('No pending key exchange')) {
|
|
286
|
-
console.log(`[E2EE] Duplicate ack
|
|
286
|
+
console.log(`[E2EE] Duplicate ack — ignored`);
|
|
287
287
|
}
|
|
288
288
|
else {
|
|
289
289
|
console.error(`[E2EE] Key exchange ack failed: ${msg}`);
|
|
@@ -520,7 +520,7 @@ class WebSocketClient extends events_1.EventEmitter {
|
|
|
520
520
|
if (sessions.length > 0) {
|
|
521
521
|
const peer = this.e2eePeerDeviceId;
|
|
522
522
|
const hasE2EE = peer ? this.e2eeManager?.hasSessionKey(peer) : false;
|
|
523
|
-
console.log(`[WS] sendClaudeSessions: ${sessions.length} sessions,
|
|
523
|
+
console.log(`[WS] sendClaudeSessions: ${sessions.length} sessions, e2ee=${hasE2EE}`);
|
|
524
524
|
const withDeviceId = sessions.map(s => ({ ...s, deviceId: config_1.config.deviceId }));
|
|
525
525
|
this.emitSensitive('claude_session_batch_update', { sessions: withDeviceId }, peer ?? undefined);
|
|
526
526
|
}
|
package/package.json
CHANGED