@agentunion/fastaun-browser 0.5.0 → 0.5.2
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/CHANGELOG.md +550 -464
- package/_packed_docs/CHANGELOG-validators.md +147 -0
- package/_packed_docs/CHANGELOG.md +550 -464
- package/_packed_docs/INDEX.md +201 -177
- package/_packed_docs/KITE_DOCS_GUIDE.md +38 -32
- package/_packed_docs/agent.md//350/277/234/347/250/213agent.md/347/274/223/345/255/230/344/270/216etag/351/200/217/344/274/240/346/226/271/346/241/210.md +169 -116
- package/_packed_docs/aun-perf-audit-critical-bugs.md +315 -0
- package/_packed_docs/cli/AUN-CLI/350/256/276/350/256/241/346/226/207/346/241/243.md +263 -260
- package/_packed_docs/cli/CLI/346/211/213/345/206/214.md +331 -0
- package/_packed_docs/protocol/00-/346/200/273/350/247/210/344/270/216/345/210/206/345/261/202.md +2 -2
- package/_packed_docs/protocol/00A-/350/256/276/350/256/241/345/216/237/345/210/231-/344/270/272Agent/350/200/214/347/224/237.md +1 -1
- package/_packed_docs/protocol/01-/350/272/253/344/273/275/344/270/216/345/207/255/350/257/201/345/215/217/350/256/256-auth.md +39 -16
- package/_packed_docs/protocol/03-Gateway-/350/277/236/346/216/245/346/250/241/345/274/217.md +8 -5
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +18 -19
- package/_packed_docs/protocol/07-/351/224/231/350/257/257/347/240/201/344/270/216/347/212/266/346/200/201/346/234/272.md +1 -1
- package/_packed_docs/protocol/08-AUN-E2EE-Group.md +139 -746
- package/_packed_docs/protocol/08-AUN-E2EE.md +12 -10
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +117 -171
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +6 -0
- package/_packed_docs/protocol/15-/347/246/273/347/272/277/346/216/250/351/200/201/351/200/232/347/237/245/345/215/217/350/256/256.md +1 -1
- package/_packed_docs/protocol/16-/347/263/273/347/273/237/347/233/256/345/275/225/344/277/235/346/212/244/346/226/271/346/241/210.md +177 -0
- package/_packed_docs/protocol/README.md +5 -4
- package/_packed_docs/protocol/aun-docs-guide.md +2 -2
- package/_packed_docs/protocol/index.md +12 -7
- package/_packed_docs/protocol//350/215/211/346/241/210-/346/213/222/347/273/235/344/277/241/345/217/267/345/215/217/350/256/256.md +1 -1
- package/_packed_docs/protocol//351/231/204/345/275/225A-/346/234/257/350/257/255/350/241/250.md +13 -13
- package/_packed_docs/protocol//351/231/204/345/275/225L-E2EE/345/256/236/347/216/260/346/214/207/345/215/227.md +9 -9
- package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +15 -13
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +26 -18
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +35 -284
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +473 -442
- package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +11 -7
- package/_packed_docs/sdk/09-collab-rpc-manual.md +581 -550
- package/_packed_docs/sdk/09-group-rpc-manual.md +367 -433
- package/_packed_docs/sdk/09-message-rpc-manual.md +50 -28
- package/_packed_docs/sdk/09-payload-reference.md +3 -3
- package/_packed_docs/sdk/09-storage-rpc-manual.md +57 -20
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +18 -17
- package/_packed_docs/sdk/E2EE_V2/346/266/210/346/201/257/351/200/232/344/277/241/346/227/266/345/272/217/345/233/276.md +3 -2
- package/_packed_docs/sdk/INDEX.md +25 -24
- package/_packed_docs/sdk/Notify/351/200/232/347/237/245/346/226/271/346/241/210.md +6 -2
- package/dist/agent-md.d.ts.map +1 -1
- package/dist/agent-md.js +18 -9
- package/dist/agent-md.js.map +1 -1
- package/dist/bundle.js +1661 -1180
- package/dist/client/delivery.d.ts +13 -2
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +251 -46
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.d.ts.map +1 -1
- package/dist/client/group-state.js +36 -14
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/lifecycle.js +2 -2
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +1 -0
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +166 -50
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +14 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +376 -126
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +5 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +187 -46
- package/dist/client.js.map +1 -1
- package/dist/collab/client.d.ts +8 -0
- package/dist/collab/client.d.ts.map +1 -1
- package/dist/collab/client.js +12 -0
- package/dist/collab/client.js.map +1 -1
- package/dist/errors.d.ts +0 -20
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +8 -51
- package/dist/errors.js.map +1 -1
- package/dist/facades.d.ts +9 -4
- package/dist/facades.d.ts.map +1 -1
- package/dist/facades.js +192 -31
- package/dist/facades.js.map +1 -1
- package/dist/group-fs.d.ts +17 -0
- package/dist/group-fs.d.ts.map +1 -1
- package/dist/group-fs.js +54 -11
- package/dist/group-fs.js.map +1 -1
- package/dist/group-id.d.ts +9 -12
- package/dist/group-id.d.ts.map +1 -1
- package/dist/group-id.js +41 -63
- package/dist/group-id.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/keystore/index.d.ts +2 -54
- package/dist/keystore/index.d.ts.map +1 -1
- package/dist/keystore/indexeddb-identity-store.d.ts +3 -0
- package/dist/keystore/indexeddb-identity-store.d.ts.map +1 -1
- package/dist/keystore/indexeddb-identity-store.js +65 -0
- package/dist/keystore/indexeddb-identity-store.js.map +1 -1
- package/dist/keystore/indexeddb-shared.d.ts +3 -17
- package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
- package/dist/keystore/indexeddb-shared.js +4 -47
- package/dist/keystore/indexeddb-shared.js.map +1 -1
- package/dist/keystore/indexeddb-token-store.d.ts +1 -64
- package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
- package/dist/keystore/indexeddb-token-store.js +45 -774
- package/dist/keystore/indexeddb-token-store.js.map +1 -1
- package/dist/logger.d.ts +2 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +4 -0
- package/dist/logger.js.map +1 -1
- package/dist/storage/lowlevel.d.ts +9 -1
- package/dist/storage/lowlevel.d.ts.map +1 -1
- package/dist/storage/lowlevel.js +12 -1
- package/dist/storage/lowlevel.js.map +1 -1
- package/dist/storage/vfs.d.ts +22 -0
- package/dist/storage/vfs.d.ts.map +1 -1
- package/dist/storage/vfs.js +54 -0
- package/dist/storage/vfs.js.map +1 -1
- package/dist/tools/cross-sdk-agent.js +336 -49
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +2 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +96 -3
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +39 -56
- package/dist/types.d.ts.map +1 -1
- package/dist/v2/session/session.d.ts +2 -0
- package/dist/v2/session/session.d.ts.map +1 -1
- package/dist/v2/session/session.js +58 -22
- package/dist/v2/session/session.js.map +1 -1
- package/dist/v2/state/commitment.d.ts +1 -1
- package/dist/v2/state/commitment.d.ts.map +1 -1
- package/dist/v2/state/commitment.js +5 -3
- package/dist/v2/state/commitment.js.map +1 -1
- package/dist/validators.d.ts +35 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +127 -0
- package/dist/validators.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/_packed_docs/collab-gateway-boundary-test-report.md +0 -164
package/dist/client/v2-e2ee.js
CHANGED
|
@@ -3,9 +3,13 @@ import { E2EEError, StateError, ValidationError } from '../errors.js';
|
|
|
3
3
|
import { normalizeGroupId } from '../group-id.js';
|
|
4
4
|
import { decryptMessage, encryptGroupMessage, encryptP2PMessage, } from '../v2/e2ee/index.js';
|
|
5
5
|
import { V2Session, V2KeyStore } from '../v2/session/index.js';
|
|
6
|
-
import { isJsonObject } from '../types.js';
|
|
6
|
+
import { ConnectionState, isJsonObject } from '../types.js';
|
|
7
7
|
const V2_BOOTSTRAP_TTL_MS = 60 * 60 * 1000;
|
|
8
8
|
const V2_RETRYABLE_CODES = new Set([-33011, -33012, -33050, -33052, -33054]);
|
|
9
|
+
const V2_GROUP_STALE_BOOTSTRAP_CODE = -33054;
|
|
10
|
+
const V2_SESSION_INIT_RETRY_LIMIT = 12;
|
|
11
|
+
const V2_SESSION_INIT_RETRY_BASE_MS = 1000;
|
|
12
|
+
const V2_SESSION_INIT_RETRY_MAX_MS = 5000;
|
|
9
13
|
const MEMBERSHIP_ACTIONS = new Set([
|
|
10
14
|
'member_added',
|
|
11
15
|
'member_left',
|
|
@@ -17,6 +21,18 @@ const MEMBERSHIP_ACTIONS = new Set([
|
|
|
17
21
|
'invite_code_used',
|
|
18
22
|
]);
|
|
19
23
|
const JOIN_ACTIONS = new Set(['member_added', 'joined', 'join_approved', 'invite_code_used']);
|
|
24
|
+
function v2PullTailDelayMs(msgCount, pageLimit) {
|
|
25
|
+
const count = Math.trunc(Number(msgCount || 0));
|
|
26
|
+
const limit = Math.trunc(Number(pageLimit || 0));
|
|
27
|
+
if (count <= 0 || (limit > 0 && count >= limit))
|
|
28
|
+
return 0;
|
|
29
|
+
return Math.min(500, Math.max(5, 1000 / (count + 1)));
|
|
30
|
+
}
|
|
31
|
+
function v2Sleep(ms) {
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
globalThis.setTimeout(resolve, ms);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
20
36
|
function exactArrayBuffer(bytes) {
|
|
21
37
|
return bytes.slice().buffer;
|
|
22
38
|
}
|
|
@@ -57,6 +73,17 @@ function v2B64uToBytes(value) {
|
|
|
57
73
|
function formatE2EEError(error) {
|
|
58
74
|
return error instanceof Error ? error : String(error);
|
|
59
75
|
}
|
|
76
|
+
function isTransientV2SessionInitError(error) {
|
|
77
|
+
const text = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
78
|
+
return text.includes('no_service')
|
|
79
|
+
|| text.includes('service plane unavailable')
|
|
80
|
+
|| text.includes('transport not connected')
|
|
81
|
+
|| text.includes('transport closed')
|
|
82
|
+
|| text.includes('websocket closed')
|
|
83
|
+
|| text.includes('websocket is not open')
|
|
84
|
+
|| text.includes('connection closed')
|
|
85
|
+
|| text.includes('socket closed');
|
|
86
|
+
}
|
|
60
87
|
function uuidV4() {
|
|
61
88
|
if (typeof crypto.randomUUID === 'function')
|
|
62
89
|
return crypto.randomUUID();
|
|
@@ -203,6 +230,10 @@ function applyV2WrapPolicyToTargets(targets, policy) {
|
|
|
203
230
|
}
|
|
204
231
|
export class V2E2EECoordinator {
|
|
205
232
|
runtime;
|
|
233
|
+
targetSetCache = new Map();
|
|
234
|
+
v2SessionInitRetryTimer = null;
|
|
235
|
+
v2SessionInitRetryBackgroundSync = false;
|
|
236
|
+
v2SessionInitRetryIdentityKey = null;
|
|
206
237
|
constructor(runtime) {
|
|
207
238
|
this.runtime = runtime;
|
|
208
239
|
}
|
|
@@ -224,29 +255,151 @@ export class V2E2EECoordinator {
|
|
|
224
255
|
}
|
|
225
256
|
deleteBootstrapCacheEntry(key) {
|
|
226
257
|
this.bootstrapCache.delete(key);
|
|
258
|
+
this.targetSetCache.delete(key);
|
|
227
259
|
}
|
|
228
260
|
clearBootstrapCache() {
|
|
229
261
|
this.bootstrapCache.clear();
|
|
262
|
+
this.targetSetCache.clear();
|
|
230
263
|
}
|
|
231
264
|
pruneExpiredBootstrapCache(ttlMs, now = Date.now()) {
|
|
232
265
|
for (const [key, entry] of this.bootstrapCache) {
|
|
233
266
|
if (now - Number(entry.cachedAt ?? 0) >= ttlMs) {
|
|
234
267
|
this.bootstrapCache.delete(key);
|
|
268
|
+
this.targetSetCache.delete(key);
|
|
235
269
|
}
|
|
236
270
|
}
|
|
271
|
+
for (const [key, entry] of this.targetSetCache) {
|
|
272
|
+
if (now - Number(entry.cachedAt ?? 0) >= ttlMs) {
|
|
273
|
+
this.targetSetCache.delete(key);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
getTargetSetCache(key) {
|
|
278
|
+
const cached = this.targetSetCache.get(key);
|
|
279
|
+
if (!cached || Date.now() - cached.cachedAt >= V2_BOOTSTRAP_TTL_MS) {
|
|
280
|
+
if (cached)
|
|
281
|
+
this.targetSetCache.delete(key);
|
|
282
|
+
return undefined;
|
|
283
|
+
}
|
|
284
|
+
return cached.value;
|
|
285
|
+
}
|
|
286
|
+
setTargetSetCache(key, value) {
|
|
287
|
+
this.targetSetCache.set(key, { cachedAt: Date.now(), value });
|
|
288
|
+
return value;
|
|
289
|
+
}
|
|
290
|
+
cloneP2PTargetSet(value) {
|
|
291
|
+
return {
|
|
292
|
+
targets: value.targets.map((target) => ({ ...target })),
|
|
293
|
+
auditRecipients: value.auditRecipients.map((target) => ({ ...target })),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
cloneGroupTargetSet(value) {
|
|
297
|
+
return {
|
|
298
|
+
targets: value.targets.map((target) => ({ ...target })),
|
|
299
|
+
};
|
|
237
300
|
}
|
|
238
301
|
async onConnected(opts) {
|
|
239
302
|
const client = this.client;
|
|
240
303
|
try {
|
|
241
304
|
await client._initV2Session();
|
|
305
|
+
this.clearV2SessionInitRetry();
|
|
242
306
|
}
|
|
243
307
|
catch (exc) {
|
|
244
308
|
client._clientLog.warn(`V2 session init failed (non-fatal): ${formatE2EEError(exc)}`);
|
|
309
|
+
if (isTransientV2SessionInitError(exc)) {
|
|
310
|
+
this.scheduleV2SessionInitRetry(1, opts.backgroundSync, exc);
|
|
311
|
+
}
|
|
245
312
|
}
|
|
246
313
|
if (opts.backgroundSync && client._v2Session && typeof client._v2AutoConfirmPendingProposals === 'function') {
|
|
247
314
|
client._safeAsync(client._v2AutoConfirmPendingProposals());
|
|
248
315
|
}
|
|
249
316
|
}
|
|
317
|
+
currentV2SessionInitRetryIdentityKey() {
|
|
318
|
+
const client = this.client;
|
|
319
|
+
if (!client._aid)
|
|
320
|
+
return null;
|
|
321
|
+
return JSON.stringify([
|
|
322
|
+
client._aid,
|
|
323
|
+
client._deviceId ?? '',
|
|
324
|
+
client._currentAid ?? null,
|
|
325
|
+
Number(client._v2RuntimeGeneration ?? 0),
|
|
326
|
+
]);
|
|
327
|
+
}
|
|
328
|
+
canRetryV2SessionInit(identityKey) {
|
|
329
|
+
const client = this.client;
|
|
330
|
+
return client.state === ConnectionState.READY
|
|
331
|
+
&& !client._closing
|
|
332
|
+
&& this.currentV2SessionInitRetryIdentityKey() === identityKey;
|
|
333
|
+
}
|
|
334
|
+
clearV2SessionInitRetry() {
|
|
335
|
+
if (this.v2SessionInitRetryTimer) {
|
|
336
|
+
globalThis.clearTimeout(this.v2SessionInitRetryTimer);
|
|
337
|
+
this.v2SessionInitRetryTimer = null;
|
|
338
|
+
}
|
|
339
|
+
this.v2SessionInitRetryBackgroundSync = false;
|
|
340
|
+
this.v2SessionInitRetryIdentityKey = null;
|
|
341
|
+
}
|
|
342
|
+
scheduleV2SessionInitRetry(attempt, backgroundSync, reason) {
|
|
343
|
+
const client = this.client;
|
|
344
|
+
if (attempt > V2_SESSION_INIT_RETRY_LIMIT) {
|
|
345
|
+
this.v2SessionInitRetryBackgroundSync = false;
|
|
346
|
+
this.v2SessionInitRetryIdentityKey = null;
|
|
347
|
+
client._clientLog.warn(`V2 session init retry exhausted: attempts=${V2_SESSION_INIT_RETRY_LIMIT} last_error=${formatE2EEError(reason)}`);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
const identityKey = this.currentV2SessionInitRetryIdentityKey();
|
|
351
|
+
if (!identityKey || !this.canRetryV2SessionInit(identityKey))
|
|
352
|
+
return;
|
|
353
|
+
if (this.v2SessionInitRetryIdentityKey && this.v2SessionInitRetryIdentityKey !== identityKey) {
|
|
354
|
+
this.clearV2SessionInitRetry();
|
|
355
|
+
}
|
|
356
|
+
if (this.v2SessionInitRetryTimer) {
|
|
357
|
+
this.v2SessionInitRetryBackgroundSync = this.v2SessionInitRetryBackgroundSync || backgroundSync;
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
this.v2SessionInitRetryIdentityKey = identityKey;
|
|
361
|
+
this.v2SessionInitRetryBackgroundSync = this.v2SessionInitRetryBackgroundSync || backgroundSync;
|
|
362
|
+
const delayMs = Math.min(V2_SESSION_INIT_RETRY_BASE_MS * attempt, V2_SESSION_INIT_RETRY_MAX_MS);
|
|
363
|
+
client._clientLog.debug(`V2 session init retry scheduled: attempt=${attempt} delay_ms=${delayMs} reason=${formatE2EEError(reason)}`);
|
|
364
|
+
this.v2SessionInitRetryTimer = globalThis.setTimeout(() => {
|
|
365
|
+
this.v2SessionInitRetryTimer = null;
|
|
366
|
+
if (!this.canRetryV2SessionInit(identityKey)) {
|
|
367
|
+
this.clearV2SessionInitRetry();
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
client._safeAsync(this.retryV2SessionInit(attempt, identityKey));
|
|
371
|
+
}, delayMs);
|
|
372
|
+
}
|
|
373
|
+
async retryV2SessionInit(attempt, identityKey) {
|
|
374
|
+
const client = this.client;
|
|
375
|
+
if (!this.canRetryV2SessionInit(identityKey)) {
|
|
376
|
+
this.clearV2SessionInitRetry();
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
try {
|
|
380
|
+
await client._initV2Session();
|
|
381
|
+
const backgroundSync = this.v2SessionInitRetryBackgroundSync;
|
|
382
|
+
this.clearV2SessionInitRetry();
|
|
383
|
+
client._clientLog.debug(`V2 session init retry succeeded: attempt=${attempt}`);
|
|
384
|
+
if (backgroundSync && client._v2Session) {
|
|
385
|
+
if (typeof client._v2AutoConfirmPendingProposals === 'function') {
|
|
386
|
+
client._safeAsync(client._v2AutoConfirmPendingProposals());
|
|
387
|
+
}
|
|
388
|
+
if (typeof client._fillP2pGap === 'function') {
|
|
389
|
+
client._safeAsync(client._fillP2pGap());
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
catch (exc) {
|
|
394
|
+
client._clientLog.warn(`V2 session init retry failed: attempt=${attempt} err=${formatE2EEError(exc)}`);
|
|
395
|
+
if (isTransientV2SessionInitError(exc)) {
|
|
396
|
+
this.scheduleV2SessionInitRetry(attempt + 1, this.v2SessionInitRetryBackgroundSync, exc);
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
this.clearV2SessionInitRetry();
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
250
403
|
async initV2Session() {
|
|
251
404
|
const client = this.client;
|
|
252
405
|
if (!client._aid)
|
|
@@ -563,15 +716,28 @@ export class V2E2EECoordinator {
|
|
|
563
716
|
const decrypted = [];
|
|
564
717
|
let nextAfterSeq = opts?.force ? afterSeq : (afterSeq || (ns ? client._seqTracker.getContiguousSeq(ns) : 0));
|
|
565
718
|
let pageCount = 0;
|
|
719
|
+
let lastAutoAckSeq = 0;
|
|
720
|
+
let pendingAckSeq = 0;
|
|
566
721
|
const maxPages = 100;
|
|
567
722
|
while (pageCount < maxPages) {
|
|
568
723
|
pageCount += 1;
|
|
724
|
+
const ackUpToSeq = pendingAckSeq;
|
|
569
725
|
const result = await client._callRawV2Rpc('message.v2.pull', {
|
|
570
726
|
after_seq: nextAfterSeq,
|
|
571
727
|
limit,
|
|
572
728
|
...(opts?.force ? { force: true } : {}),
|
|
729
|
+
...(ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}),
|
|
573
730
|
});
|
|
574
|
-
|
|
731
|
+
if (ackUpToSeq > 0) {
|
|
732
|
+
pendingAckSeq = 0;
|
|
733
|
+
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
734
|
+
}
|
|
735
|
+
const pageMessages = (Array.isArray(result?.messages) ? result.messages : []);
|
|
736
|
+
const messages = pageMessages.filter((msg) => {
|
|
737
|
+
const seq = Number(msg.seq ?? 0);
|
|
738
|
+
return Number.isFinite(seq) && seq > nextAfterSeq;
|
|
739
|
+
});
|
|
740
|
+
client._clientLog.debug(`message.v2.pull page response: page=${pageCount}, raw_count=${messages.length}, stale_count=${Math.max(0, pageMessages.length - messages.length)}, has_more=${String(result.has_more ?? '')}, server_ack_seq=${String(result.server_ack_seq ?? '')}`);
|
|
575
741
|
const seqs = messages
|
|
576
742
|
.map((msg) => Number(msg.seq ?? 0))
|
|
577
743
|
.filter((seq) => Number.isFinite(seq) && seq > 0);
|
|
@@ -650,22 +816,43 @@ export class V2E2EECoordinator {
|
|
|
650
816
|
const contigAdvanced = ackSeq !== pageContigBefore;
|
|
651
817
|
if (contigAdvanced) {
|
|
652
818
|
await client._drainOrderedMessages(ns);
|
|
653
|
-
client.
|
|
819
|
+
client._persistSeq(ns);
|
|
654
820
|
}
|
|
655
|
-
const ackNeeded =
|
|
821
|
+
const ackNeeded = pageMessages.length > 0
|
|
656
822
|
&& ackSeq > 0
|
|
823
|
+
&& ackSeq > lastAutoAckSeq
|
|
657
824
|
&& (contigAdvanced || (hasServerAckSeq && ackSeq > serverAckSeq));
|
|
658
825
|
if (ackNeeded) {
|
|
659
|
-
|
|
826
|
+
const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
|
|
827
|
+
const canContinuePage = messages.length > 0 && nextAfter > nextAfterSeq;
|
|
828
|
+
if (canContinuePage) {
|
|
829
|
+
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
830
|
+
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
await this.ackV2(ackSeq);
|
|
834
|
+
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
835
|
+
}
|
|
660
836
|
}
|
|
661
837
|
}
|
|
662
838
|
const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
|
|
663
|
-
|
|
839
|
+
const rawCount = messages.length;
|
|
840
|
+
const canContinuePage = rawCount > 0 && nextAfter > nextAfterSeq;
|
|
841
|
+
const fullPage = canContinuePage && limit > 0 && rawCount >= limit;
|
|
842
|
+
const shouldContinue = canContinuePage && (fullPage || pendingAckSeq > 0);
|
|
843
|
+
if (!shouldContinue)
|
|
664
844
|
break;
|
|
845
|
+
const tailDelayMs = v2PullTailDelayMs(rawCount, limit);
|
|
846
|
+
if (tailDelayMs > 0)
|
|
847
|
+
await v2Sleep(tailDelayMs);
|
|
665
848
|
nextAfterSeq = nextAfter;
|
|
666
849
|
}
|
|
667
850
|
if (pageCount >= maxPages) {
|
|
668
851
|
client._clientLog.warn(`message.v2.pull reached max_pages=${maxPages} after_seq=${nextAfterSeq}`);
|
|
852
|
+
if (pendingAckSeq > 0) {
|
|
853
|
+
await this.ackV2(pendingAckSeq);
|
|
854
|
+
pendingAckSeq = 0;
|
|
855
|
+
}
|
|
669
856
|
}
|
|
670
857
|
return decrypted;
|
|
671
858
|
}
|
|
@@ -737,7 +924,7 @@ export class V2E2EECoordinator {
|
|
|
737
924
|
const ns = `group:${gid}`;
|
|
738
925
|
client._seqTracker.onMessageSeq(ns, seq);
|
|
739
926
|
client._markPublishedSeq(ns, seq);
|
|
740
|
-
client._saveSeqTrackerState();
|
|
927
|
+
await client._saveSeqTrackerState();
|
|
741
928
|
}
|
|
742
929
|
}
|
|
743
930
|
return client._delivery.attachSendResultEnvelope('group.send', {
|
|
@@ -761,7 +948,7 @@ export class V2E2EECoordinator {
|
|
|
761
948
|
const ns = `group:${gid}`;
|
|
762
949
|
client._seqTracker.onMessageSeq(ns, seq);
|
|
763
950
|
client._markPublishedSeq(ns, seq);
|
|
764
|
-
client._saveSeqTrackerState();
|
|
951
|
+
await client._saveSeqTrackerState();
|
|
765
952
|
}
|
|
766
953
|
}
|
|
767
954
|
return client._delivery.attachSendResultEnvelope('group.send', {
|
|
@@ -787,20 +974,34 @@ export class V2E2EECoordinator {
|
|
|
787
974
|
throw new ValidationError('group.pull requires group_id');
|
|
788
975
|
const ns = `group:${gid}`;
|
|
789
976
|
const decrypted = [];
|
|
977
|
+
const wireGroupId = String(opts?.wireGroupId ?? groupId ?? '').trim() || gid;
|
|
790
978
|
const cursorParams = opts?.cursorParams ?? {};
|
|
791
979
|
const ownsCursor = opts?.ownsCursor !== false;
|
|
792
980
|
let nextAfterSeq = opts?.explicitAfterSeq ? afterSeq : (afterSeq || client._seqTracker.getContiguousSeq(ns));
|
|
793
981
|
let pageCount = 0;
|
|
982
|
+
let lastAutoAckSeq = 0;
|
|
983
|
+
let pendingAckSeq = 0;
|
|
794
984
|
const maxPages = 100;
|
|
795
985
|
while (pageCount < maxPages) {
|
|
796
986
|
pageCount += 1;
|
|
987
|
+
const ackUpToSeq = ownsCursor ? pendingAckSeq : 0;
|
|
797
988
|
const result = await client._callRawV2Rpc('group.v2.pull', {
|
|
798
|
-
group_id:
|
|
989
|
+
group_id: wireGroupId,
|
|
990
|
+
group_aid: gid,
|
|
799
991
|
after_seq: nextAfterSeq,
|
|
800
992
|
limit,
|
|
801
993
|
...cursorParams,
|
|
994
|
+
...(ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}),
|
|
995
|
+
});
|
|
996
|
+
if (ackUpToSeq > 0) {
|
|
997
|
+
pendingAckSeq = 0;
|
|
998
|
+
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
999
|
+
}
|
|
1000
|
+
const pageMessages = (Array.isArray(result?.messages) ? result.messages : []);
|
|
1001
|
+
const messages = pageMessages.filter((msg) => {
|
|
1002
|
+
const seq = Number(msg.seq ?? 0);
|
|
1003
|
+
return Number.isFinite(seq) && seq > nextAfterSeq;
|
|
802
1004
|
});
|
|
803
|
-
const messages = (Array.isArray(result?.messages) ? result.messages : []);
|
|
804
1005
|
const seqs = messages
|
|
805
1006
|
.map((msg) => Number(msg.seq ?? 0))
|
|
806
1007
|
.filter((seq) => Number.isFinite(seq) && seq > 0);
|
|
@@ -886,25 +1087,46 @@ export class V2E2EECoordinator {
|
|
|
886
1087
|
const contigAdvanced = ackSeq !== pageContigBefore;
|
|
887
1088
|
if (contigAdvanced) {
|
|
888
1089
|
await client._drainOrderedMessages(ns);
|
|
889
|
-
client.
|
|
1090
|
+
client._persistSeq(ns);
|
|
890
1091
|
}
|
|
891
1092
|
const hasServerCursor = cursor !== null && Object.prototype.hasOwnProperty.call(cursor, 'current_seq');
|
|
892
|
-
const ackNeeded =
|
|
1093
|
+
const ackNeeded = pageMessages.length > 0
|
|
893
1094
|
&& ackSeq > 0
|
|
1095
|
+
&& ackSeq > lastAutoAckSeq
|
|
894
1096
|
&& ownsCursor
|
|
895
1097
|
&& (contigAdvanced || (hasServerCursor && ackSeq > cursorCurrentSeq));
|
|
896
1098
|
if (ackNeeded) {
|
|
897
|
-
|
|
1099
|
+
const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
|
|
1100
|
+
const canContinuePage = messages.length > 0 && nextAfter > nextAfterSeq && ownsCursor;
|
|
1101
|
+
if (canContinuePage) {
|
|
1102
|
+
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
1103
|
+
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
1104
|
+
}
|
|
1105
|
+
else {
|
|
1106
|
+
await this.ackGroupV2(gid, ackSeq);
|
|
1107
|
+
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
1108
|
+
}
|
|
898
1109
|
}
|
|
899
1110
|
const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
|
|
900
1111
|
if (!ownsCursor)
|
|
901
1112
|
break;
|
|
902
|
-
|
|
1113
|
+
const rawCount = messages.length;
|
|
1114
|
+
const canContinuePage = rawCount > 0 && nextAfter > nextAfterSeq;
|
|
1115
|
+
const fullPage = canContinuePage && limit > 0 && rawCount >= limit;
|
|
1116
|
+
const shouldContinue = canContinuePage && (fullPage || pendingAckSeq > 0);
|
|
1117
|
+
if (!shouldContinue)
|
|
903
1118
|
break;
|
|
1119
|
+
const tailDelayMs = v2PullTailDelayMs(rawCount, limit);
|
|
1120
|
+
if (tailDelayMs > 0)
|
|
1121
|
+
await v2Sleep(tailDelayMs);
|
|
904
1122
|
nextAfterSeq = nextAfter;
|
|
905
1123
|
}
|
|
906
1124
|
if (pageCount >= maxPages) {
|
|
907
1125
|
client._clientLog.warn(`group.v2.pull reached max_pages=${maxPages} group=${gid} after_seq=${nextAfterSeq}`);
|
|
1126
|
+
if (pendingAckSeq > 0) {
|
|
1127
|
+
await this.ackGroupV2(gid, pendingAckSeq);
|
|
1128
|
+
pendingAckSeq = 0;
|
|
1129
|
+
}
|
|
908
1130
|
}
|
|
909
1131
|
return decrypted;
|
|
910
1132
|
}
|
|
@@ -918,7 +1140,7 @@ export class V2E2EECoordinator {
|
|
|
918
1140
|
if (!Number.isFinite(seq) || seq <= 0)
|
|
919
1141
|
return { acked: 0 };
|
|
920
1142
|
seq = client._clampAckSeq('group.v2.ack', 'up_to_seq', ns, seq);
|
|
921
|
-
return client._callRawV2Rpc('group.v2.ack', { group_id: gid, up_to_seq: seq });
|
|
1143
|
+
return client._callRawV2Rpc('group.v2.ack', { group_id: gid, group_aid: gid, up_to_seq: seq });
|
|
922
1144
|
}
|
|
923
1145
|
async putMessageThoughtEncryptedV2(params) {
|
|
924
1146
|
const client = this.client;
|
|
@@ -1429,6 +1651,7 @@ export class V2E2EECoordinator {
|
|
|
1429
1651
|
wrapPolicy = cached.wrapPolicy;
|
|
1430
1652
|
}
|
|
1431
1653
|
else {
|
|
1654
|
+
this.targetSetCache.delete(to);
|
|
1432
1655
|
const bs = await client.call('message.v2.bootstrap', {
|
|
1433
1656
|
peer_aid: to,
|
|
1434
1657
|
e2ee_wrap_capabilities: v2WrapCapabilities(),
|
|
@@ -1448,73 +1671,85 @@ export class V2E2EECoordinator {
|
|
|
1448
1671
|
if (peerDevices.length === 0) {
|
|
1449
1672
|
throw new E2EEError(`V2 bootstrap: no devices found for ${to}`);
|
|
1450
1673
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
const target = await client._v2BuildTargetFromDevice({
|
|
1455
|
-
dev,
|
|
1456
|
-
aid: to,
|
|
1457
|
-
deviceId: devId.value,
|
|
1458
|
-
role: 'peer',
|
|
1459
|
-
defaultKeySource: 'peer_device_prekey',
|
|
1460
|
-
});
|
|
1461
|
-
if (target)
|
|
1462
|
-
targets.push(target);
|
|
1463
|
-
}
|
|
1464
|
-
for (const dev of auditRaw) {
|
|
1465
|
-
const target = await client._v2BuildTargetFromDevice({
|
|
1466
|
-
dev,
|
|
1467
|
-
aid: String(dev.aid ?? ''),
|
|
1468
|
-
deviceId: String(dev.device_id ?? ''),
|
|
1469
|
-
role: 'audit',
|
|
1470
|
-
defaultKeySource: 'peer_device_prekey',
|
|
1471
|
-
});
|
|
1472
|
-
if (target)
|
|
1473
|
-
targets.push(target);
|
|
1674
|
+
let targetSet = useCache ? this.getTargetSetCache(to) : undefined;
|
|
1675
|
+
if (targetSet) {
|
|
1676
|
+
targetSet = this.cloneP2PTargetSet(targetSet);
|
|
1474
1677
|
}
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1678
|
+
else {
|
|
1679
|
+
const targets = [];
|
|
1680
|
+
for (const dev of peerDevices) {
|
|
1681
|
+
const devId = getV2DeviceId(dev);
|
|
1682
|
+
const target = await client._v2BuildTargetFromDevice({
|
|
1683
|
+
dev,
|
|
1684
|
+
aid: to,
|
|
1685
|
+
deviceId: devId.value,
|
|
1686
|
+
role: 'peer',
|
|
1687
|
+
defaultKeySource: 'peer_device_prekey',
|
|
1688
|
+
});
|
|
1689
|
+
if (target)
|
|
1690
|
+
targets.push(target);
|
|
1691
|
+
}
|
|
1692
|
+
const auditTargets = [];
|
|
1693
|
+
for (const dev of auditRaw) {
|
|
1694
|
+
const target = await client._v2BuildTargetFromDevice({
|
|
1695
|
+
dev,
|
|
1696
|
+
aid: String(dev.aid ?? ''),
|
|
1697
|
+
deviceId: String(dev.device_id ?? ''),
|
|
1698
|
+
role: 'audit',
|
|
1699
|
+
defaultKeySource: 'peer_device_prekey',
|
|
1700
|
+
});
|
|
1701
|
+
if (target)
|
|
1702
|
+
auditTargets.push(target);
|
|
1703
|
+
}
|
|
1704
|
+
if (client._aid && client._aid !== to) {
|
|
1705
|
+
try {
|
|
1706
|
+
const selfCached = this.getBootstrapCacheEntry(client._aid);
|
|
1707
|
+
let selfDevices = [];
|
|
1708
|
+
if (selfCached && (Date.now() - Number(selfCached.cachedAt ?? 0)) < V2_BOOTSTRAP_TTL_MS) {
|
|
1709
|
+
selfDevices = (selfCached.devices ?? []);
|
|
1710
|
+
}
|
|
1711
|
+
else {
|
|
1712
|
+
this.targetSetCache.delete(client._aid);
|
|
1713
|
+
const selfBs = await client.call('message.v2.bootstrap', {
|
|
1714
|
+
peer_aid: client._aid,
|
|
1715
|
+
e2ee_wrap_capabilities: v2WrapCapabilities(),
|
|
1716
|
+
});
|
|
1717
|
+
selfDevices = (Array.isArray(selfBs?.peer_devices) ? selfBs.peer_devices : []);
|
|
1718
|
+
if (selfDevices.length > 0) {
|
|
1719
|
+
this.setBootstrapCacheEntry(client._aid, {
|
|
1720
|
+
devices: selfDevices,
|
|
1721
|
+
auditRecipients: [],
|
|
1722
|
+
cachedAt: Date.now(),
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
for (const dev of selfDevices) {
|
|
1727
|
+
const devId = getV2DeviceId(dev);
|
|
1728
|
+
if (!devId.present || devId.value === client._deviceId)
|
|
1729
|
+
continue;
|
|
1730
|
+
const target = await client._v2BuildTargetFromDevice({
|
|
1731
|
+
dev,
|
|
1732
|
+
aid: client._aid,
|
|
1733
|
+
deviceId: devId.value,
|
|
1734
|
+
role: 'self_sync',
|
|
1735
|
+
defaultKeySource: 'peer_device_prekey',
|
|
1493
1736
|
});
|
|
1737
|
+
if (target)
|
|
1738
|
+
targets.push(target);
|
|
1494
1739
|
}
|
|
1495
1740
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
if (!devId.present || devId.value === client._deviceId)
|
|
1499
|
-
continue;
|
|
1500
|
-
const target = await client._v2BuildTargetFromDevice({
|
|
1501
|
-
dev,
|
|
1502
|
-
aid: client._aid,
|
|
1503
|
-
deviceId: devId.value,
|
|
1504
|
-
role: 'self_sync',
|
|
1505
|
-
defaultKeySource: 'peer_device_prekey',
|
|
1506
|
-
});
|
|
1507
|
-
if (target)
|
|
1508
|
-
targets.push(target);
|
|
1741
|
+
catch (exc) {
|
|
1742
|
+
client._clientLog.debug(`V2 thought self-sync bootstrap failed (non-fatal): ${String(exc)}`);
|
|
1509
1743
|
}
|
|
1510
1744
|
}
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1745
|
+
targetSet = this.setTargetSetCache(to, {
|
|
1746
|
+
targets: applyV2WrapPolicyToTargets(targets, wrapPolicy),
|
|
1747
|
+
auditRecipients: applyV2WrapPolicyToTargets(auditTargets, wrapPolicy),
|
|
1748
|
+
});
|
|
1749
|
+
targetSet = this.cloneP2PTargetSet(targetSet);
|
|
1514
1750
|
}
|
|
1515
1751
|
const sender = await session.getSenderIdentity();
|
|
1516
|
-
const
|
|
1517
|
-
const envelope = await encryptP2PMessage(sender, { targets: sendTargets, auditRecipients: [] }, opts.payload, { messageId: opts.messageId, timestamp: opts.timestamp, protectedHeaders: opts.protectedHeaders, context: opts.context });
|
|
1752
|
+
const envelope = await encryptP2PMessage(sender, targetSet, opts.payload, { messageId: opts.messageId, timestamp: opts.timestamp, protectedHeaders: opts.protectedHeaders, context: opts.context });
|
|
1518
1753
|
return envelope;
|
|
1519
1754
|
}
|
|
1520
1755
|
async buildV2GroupEnvelope(opts) {
|
|
@@ -1523,7 +1758,8 @@ export class V2E2EECoordinator {
|
|
|
1523
1758
|
throw new StateError('V2 session not initialized');
|
|
1524
1759
|
}
|
|
1525
1760
|
const session = client._v2Session;
|
|
1526
|
-
const
|
|
1761
|
+
const rawGroupId = String(opts.groupId ?? '').trim();
|
|
1762
|
+
const groupId = normalizeGroupId(rawGroupId) || rawGroupId;
|
|
1527
1763
|
if (!groupId)
|
|
1528
1764
|
throw new ValidationError("group.send requires 'group_id'");
|
|
1529
1765
|
const useCache = opts.useCache !== false;
|
|
@@ -1533,15 +1769,26 @@ export class V2E2EECoordinator {
|
|
|
1533
1769
|
let stateCommitment = { state_version: 0, state_hash: '', state_chain: '' };
|
|
1534
1770
|
let auditRecipientsRaw = [];
|
|
1535
1771
|
let wrapPolicy;
|
|
1536
|
-
|
|
1772
|
+
let usedCachedBootstrap = false;
|
|
1773
|
+
let cached = useCache ? this.getBootstrapCacheEntry(cacheKey) : undefined;
|
|
1774
|
+
if (!cached && useCache && rawGroupId && rawGroupId !== groupId) {
|
|
1775
|
+
const legacyCacheKey = `group:${rawGroupId}`;
|
|
1776
|
+
cached = this.getBootstrapCacheEntry(legacyCacheKey);
|
|
1777
|
+
if (cached) {
|
|
1778
|
+
this.setBootstrapCacheEntry(cacheKey, cached);
|
|
1779
|
+
this.deleteBootstrapCacheEntry(legacyCacheKey);
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1537
1782
|
if (cached && (Date.now() - Number(cached.cachedAt ?? 0)) < V2_BOOTSTRAP_TTL_MS) {
|
|
1538
1783
|
allDevices = (cached.devices ?? []);
|
|
1539
1784
|
epoch = Number(cached.epoch ?? 0) || 0;
|
|
1540
1785
|
stateCommitment = cached.stateCommitment ?? { state_version: 0, state_hash: '', state_chain: '' };
|
|
1541
1786
|
auditRecipientsRaw = (cached.auditRecipients ?? []);
|
|
1542
1787
|
wrapPolicy = cached.wrapPolicy;
|
|
1788
|
+
usedCachedBootstrap = true;
|
|
1543
1789
|
}
|
|
1544
1790
|
else {
|
|
1791
|
+
this.targetSetCache.delete(cacheKey);
|
|
1545
1792
|
const bs = await client.call('group.v2.bootstrap', {
|
|
1546
1793
|
group_id: groupId,
|
|
1547
1794
|
e2ee_wrap_capabilities: v2WrapCapabilities(),
|
|
@@ -1576,40 +1823,64 @@ export class V2E2EECoordinator {
|
|
|
1576
1823
|
if (allDevices.length === 0) {
|
|
1577
1824
|
throw new E2EEError(`V2 group bootstrap: no devices found for group ${groupId}`);
|
|
1578
1825
|
}
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
const
|
|
1586
|
-
|
|
1587
|
-
dev
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
role
|
|
1591
|
-
|
|
1826
|
+
let targetSet = useCache ? this.getTargetSetCache(cacheKey) : undefined;
|
|
1827
|
+
if (targetSet) {
|
|
1828
|
+
targetSet = this.cloneGroupTargetSet(targetSet);
|
|
1829
|
+
}
|
|
1830
|
+
else {
|
|
1831
|
+
const targets = [];
|
|
1832
|
+
for (const dev of allDevices) {
|
|
1833
|
+
const devAid = String(dev.aid ?? '').trim();
|
|
1834
|
+
const devId = getV2DeviceId(dev);
|
|
1835
|
+
if (devAid === client._aid && devId.present && devId.value === client._deviceId)
|
|
1836
|
+
continue;
|
|
1837
|
+
const role = devAid === client._aid ? 'self_sync' : 'member';
|
|
1838
|
+
const target = await client._v2BuildTargetFromDevice({
|
|
1839
|
+
dev,
|
|
1840
|
+
aid: devAid,
|
|
1841
|
+
deviceId: devId.value,
|
|
1842
|
+
role,
|
|
1843
|
+
defaultKeySource: 'peer_device_prekey',
|
|
1844
|
+
});
|
|
1845
|
+
if (target)
|
|
1846
|
+
targets.push(target);
|
|
1847
|
+
}
|
|
1848
|
+
if (targets.length === 0) {
|
|
1849
|
+
if (usedCachedBootstrap) {
|
|
1850
|
+
throw new E2EEError(`V2 group: no target devices for group ${groupId}; bootstrap may be stale`, {
|
|
1851
|
+
code: V2_GROUP_STALE_BOOTSTRAP_CODE,
|
|
1852
|
+
data: { expected_peer_aids: [] },
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1855
|
+
throw new E2EEError(`V2 group: no target devices for group ${groupId}`);
|
|
1856
|
+
}
|
|
1857
|
+
for (const dev of auditRecipientsRaw) {
|
|
1858
|
+
const target = await client._v2BuildTargetFromDevice({
|
|
1859
|
+
dev,
|
|
1860
|
+
aid: String(dev.aid ?? ''),
|
|
1861
|
+
deviceId: String(dev.device_id ?? ''),
|
|
1862
|
+
role: 'audit',
|
|
1863
|
+
defaultKeySource: 'peer_device_prekey',
|
|
1864
|
+
});
|
|
1865
|
+
if (target)
|
|
1866
|
+
targets.push(target);
|
|
1867
|
+
}
|
|
1868
|
+
targetSet = this.setTargetSetCache(cacheKey, {
|
|
1869
|
+
targets: applyV2WrapPolicyToTargets(targets, wrapPolicy),
|
|
1592
1870
|
});
|
|
1593
|
-
|
|
1594
|
-
targets.push(target);
|
|
1871
|
+
targetSet = this.cloneGroupTargetSet(targetSet);
|
|
1595
1872
|
}
|
|
1596
|
-
if (targets.length === 0) {
|
|
1873
|
+
if (targetSet.targets.length === 0) {
|
|
1874
|
+
if (usedCachedBootstrap) {
|
|
1875
|
+
throw new E2EEError(`V2 group: no target devices for group ${groupId}; bootstrap may be stale`, {
|
|
1876
|
+
code: V2_GROUP_STALE_BOOTSTRAP_CODE,
|
|
1877
|
+
data: { expected_peer_aids: [] },
|
|
1878
|
+
});
|
|
1879
|
+
}
|
|
1597
1880
|
throw new E2EEError(`V2 group: no target devices for group ${groupId}`);
|
|
1598
1881
|
}
|
|
1599
|
-
for (const dev of auditRecipientsRaw) {
|
|
1600
|
-
const target = await client._v2BuildTargetFromDevice({
|
|
1601
|
-
dev,
|
|
1602
|
-
aid: String(dev.aid ?? ''),
|
|
1603
|
-
deviceId: String(dev.device_id ?? ''),
|
|
1604
|
-
role: 'audit',
|
|
1605
|
-
defaultKeySource: 'peer_device_prekey',
|
|
1606
|
-
});
|
|
1607
|
-
if (target)
|
|
1608
|
-
targets.push(target);
|
|
1609
|
-
}
|
|
1610
1882
|
const sender = await session.getSenderIdentity();
|
|
1611
|
-
const
|
|
1612
|
-
const envelope = await encryptGroupMessage(sender, groupId, epoch, sendTargets, opts.payload, { messageId: opts.messageId, timestamp: opts.timestamp, protectedHeaders: opts.protectedHeaders, context: opts.context }, stateCommitment);
|
|
1883
|
+
const envelope = await encryptGroupMessage(sender, groupId, epoch, targetSet.targets, opts.payload, { messageId: opts.messageId, timestamp: opts.timestamp, protectedHeaders: opts.protectedHeaders, context: opts.context }, stateCommitment);
|
|
1613
1884
|
return envelope;
|
|
1614
1885
|
}
|
|
1615
1886
|
handleGroupChangedSpk(data, groupId, action) {
|
|
@@ -1627,26 +1898,5 @@ export class V2E2EECoordinator {
|
|
|
1627
1898
|
this.scheduleGroupSpkRotation(groupId, { reason: `group_changed:${action}` });
|
|
1628
1899
|
}
|
|
1629
1900
|
}
|
|
1630
|
-
async handleV2EpochRotated(data) {
|
|
1631
|
-
const client = this.client;
|
|
1632
|
-
if (!data || typeof data !== 'object' || Array.isArray(data))
|
|
1633
|
-
return;
|
|
1634
|
-
const d = data;
|
|
1635
|
-
const groupId = String(d.group_id ?? '').trim();
|
|
1636
|
-
if (!groupId)
|
|
1637
|
-
return;
|
|
1638
|
-
const epoch = d.epoch ?? 0;
|
|
1639
|
-
client._clientLog.debug(`_onV2EpochRotated: group=${groupId} epoch=${String(epoch)}`);
|
|
1640
|
-
this.deleteBootstrapCacheEntry(`group:${groupId}`);
|
|
1641
|
-
if (!client._v2Session)
|
|
1642
|
-
return;
|
|
1643
|
-
try {
|
|
1644
|
-
await client._v2Session.rotateSPK(client._v2CallFn());
|
|
1645
|
-
client._clientLog.info(`SPK rotated after epoch change: group=${groupId} epoch=${String(epoch)}`);
|
|
1646
|
-
}
|
|
1647
|
-
catch (exc) {
|
|
1648
|
-
client._clientLog.debug(`SPK rotation after epoch change failed (non-fatal): ${formatE2EEError(exc)}`);
|
|
1649
|
-
}
|
|
1650
|
-
}
|
|
1651
1901
|
}
|
|
1652
1902
|
//# sourceMappingURL=v2-e2ee.js.map
|