@dxos/echo-pipeline 0.3.11-main.ec7a2b3 → 0.3.11-main.ee2b64c

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 (37) hide show
  1. package/dist/lib/browser/{chunk-WDT56L4E.mjs → chunk-IOUMNVGJ.mjs} +266 -79
  2. package/dist/lib/browser/chunk-IOUMNVGJ.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +1 -3
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/testing/index.mjs +5 -5
  6. package/dist/lib/browser/testing/index.mjs.map +3 -3
  7. package/dist/lib/node/{chunk-C6GFWBRK.cjs → chunk-PDU65RAS.cjs} +260 -77
  8. package/dist/lib/node/chunk-PDU65RAS.cjs.map +7 -0
  9. package/dist/lib/node/index.cjs +26 -28
  10. package/dist/lib/node/index.cjs.map +2 -2
  11. package/dist/lib/node/meta.json +1 -1
  12. package/dist/lib/node/testing/index.cjs +20 -20
  13. package/dist/lib/node/testing/index.cjs.map +3 -3
  14. package/dist/types/src/automerge/automerge-host.d.ts +27 -2
  15. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
  16. package/dist/types/src/automerge/index.d.ts +1 -1
  17. package/dist/types/src/automerge/index.d.ts.map +1 -1
  18. package/dist/types/src/space/data-pipeline.d.ts.map +1 -1
  19. package/dist/types/src/space/space-manager.d.ts +2 -2
  20. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  21. package/dist/types/src/space/space-protocol.d.ts.map +1 -1
  22. package/dist/types/src/space/space.d.ts +1 -1
  23. package/dist/types/src/space/space.d.ts.map +1 -1
  24. package/dist/types/src/testing/database-test-rig.d.ts.map +1 -1
  25. package/package.json +33 -33
  26. package/src/automerge/automerge-host.test.ts +353 -35
  27. package/src/automerge/automerge-host.ts +138 -21
  28. package/src/automerge/index.ts +1 -1
  29. package/src/pipeline/pipeline-stress.test.ts +9 -2
  30. package/src/space/data-pipeline.ts +4 -3
  31. package/src/space/space-manager.ts +3 -3
  32. package/src/space/space-protocol.ts +4 -0
  33. package/src/space/space.ts +8 -3
  34. package/src/testing/database-test-rig.ts +4 -1
  35. package/src/testing/test-agent-builder.ts +1 -1
  36. package/dist/lib/browser/chunk-WDT56L4E.mjs.map +0 -7
  37. package/dist/lib/node/chunk-C6GFWBRK.cjs.map +0 -7
@@ -2,6 +2,8 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import { Trigger } from '@dxos/async';
6
+ import { next as automerge } from '@dxos/automerge/automerge';
5
7
  import {
6
8
  Repo,
7
9
  NetworkAdapter,
@@ -15,19 +17,27 @@ import {
15
17
  import { IndexedDBStorageAdapter } from '@dxos/automerge/automerge-repo-storage-indexeddb';
16
18
  import { Stream } from '@dxos/codec-protobuf';
17
19
  import { invariant } from '@dxos/invariant';
20
+ import { PublicKey } from '@dxos/keys';
18
21
  import { log } from '@dxos/log';
19
22
  import { type HostInfo, type SyncRepoRequest, type SyncRepoResponse } from '@dxos/protocols/proto/dxos/echo/service';
20
23
  import { type PeerInfo } from '@dxos/protocols/proto/dxos/mesh/teleport/automerge';
21
24
  import { StorageType, type Directory } from '@dxos/random-access-storage';
22
25
  import { AutomergeReplicator } from '@dxos/teleport-extension-automerge-replicator';
23
- import { arrayToBuffer, bufferToArray } from '@dxos/util';
26
+ import { trace } from '@dxos/tracing';
27
+ import { ComplexMap, ComplexSet, arrayToBuffer, bufferToArray, defaultMap, mapValues } from '@dxos/util';
24
28
 
29
+ @trace.resource()
25
30
  export class AutomergeHost {
26
31
  private readonly _repo: Repo;
27
32
  private readonly _meshNetwork: MeshNetworkAdapter;
28
33
  private readonly _clientNetwork: LocalHostNetworkAdapter;
29
34
  private readonly _storage: StorageAdapter;
30
35
 
36
+ /**
37
+ * spaceKey -> deviceKey[]
38
+ */
39
+ private readonly _authorizedDevices = new ComplexMap<PublicKey, ComplexSet<PublicKey>>(PublicKey.hash);
40
+
31
41
  constructor(storageDirectory: Directory) {
32
42
  this._meshNetwork = new MeshNetworkAdapter();
33
43
  this._clientNetwork = new LocalHostNetworkAdapter();
@@ -38,11 +48,52 @@ export class AutomergeHost {
38
48
  ? new IndexedDBStorageAdapter(storageDirectory.path, 'data')
39
49
  : new AutomergeStorageAdapter(storageDirectory);
40
50
  this._repo = new Repo({
51
+ peerId: `host-${PublicKey.random().toHex()}` as PeerId,
41
52
  network: [this._clientNetwork, this._meshNetwork],
42
53
  storage: this._storage,
43
54
 
44
55
  // TODO(dmaretskyi): Share based on HALO permissions and space affinity.
45
- sharePolicy: async (peerId, documentId) => true, // Share everything.
56
+ // Hosts, running in the worker, don't share documents unless requested by other peers.
57
+ sharePolicy: async (peerId /* device key */, documentId /* space key */) => {
58
+ if (peerId.startsWith('client-')) {
59
+ return true;
60
+ }
61
+
62
+ if (!documentId) {
63
+ return false;
64
+ }
65
+
66
+ const doc = this._repo.handles[documentId]?.docSync();
67
+ if (!doc) {
68
+ log('doc not found for share policy check', { peerId, documentId });
69
+ return false;
70
+ }
71
+
72
+ try {
73
+ if (!doc.experimental_spaceKey) {
74
+ log('space key not found for share policy check', { peerId, documentId });
75
+ return false;
76
+ }
77
+
78
+ const spaceKey = PublicKey.from(doc.experimental_spaceKey);
79
+ const authorizedDevices = this._authorizedDevices.get(spaceKey);
80
+
81
+ // TODO(mykola): Hack, stop abusing `peerMetadata` field.
82
+ const deviceKeyHex = (this.repo.peerMetadataByPeerId[peerId] as any)?.dxos_deviceKey;
83
+ if (!deviceKeyHex) {
84
+ log('device key not found for share policy check', { peerId, documentId });
85
+ return false;
86
+ }
87
+ const deviceKey = PublicKey.from(deviceKeyHex);
88
+
89
+ const isAuthorized = authorizedDevices?.has(deviceKey) ?? false;
90
+ log('share policy check', { peerId, documentId, deviceKey, spaceKey, isAuthorized });
91
+ return isAuthorized;
92
+ } catch (err) {
93
+ log.catch(err);
94
+ return false;
95
+ }
96
+ }, // Share everything.
46
97
  });
47
98
  this._clientNetwork.ready();
48
99
  this._meshNetwork.ready();
@@ -52,7 +103,22 @@ export class AutomergeHost {
52
103
  return this._repo;
53
104
  }
54
105
 
106
+ @trace.info({ depth: null })
107
+ private _automergeDocs() {
108
+ return mapValues(this._repo.handles, (handle) => ({
109
+ state: handle.state,
110
+ hasDoc: !!handle.docSync(),
111
+ heads: handle.docSync() ? automerge.getHeads(handle.docSync()) : null,
112
+ }));
113
+ }
114
+
115
+ @trace.info({ depth: null })
116
+ private _automergePeers() {
117
+ return this._repo.peers;
118
+ }
119
+
55
120
  async close() {
121
+ this._storage instanceof AutomergeStorageAdapter && (await this._storage.close());
56
122
  await this._clientNetwork.close();
57
123
  }
58
124
 
@@ -68,7 +134,7 @@ export class AutomergeHost {
68
134
  return this._clientNetwork.sendSyncMessage(request);
69
135
  }
70
136
 
71
- getHostInfo(): HostInfo {
137
+ async getHostInfo(): Promise<HostInfo> {
72
138
  return this._clientNetwork.getHostInfo();
73
139
  }
74
140
 
@@ -79,6 +145,10 @@ export class AutomergeHost {
79
145
  createExtension(): AutomergeReplicator {
80
146
  return this._meshNetwork.createExtension();
81
147
  }
148
+
149
+ authorizeDevice(spaceKey: PublicKey, deviceKey: PublicKey) {
150
+ defaultMap(this._authorizedDevices, spaceKey, () => new ComplexSet(PublicKey.hash)).add(deviceKey);
151
+ }
82
152
  }
83
153
 
84
154
  type ClientSyncState = {
@@ -104,8 +174,11 @@ class LocalHostNetworkAdapter extends NetworkAdapter {
104
174
  });
105
175
  }
106
176
 
177
+ private _connected = new Trigger();
178
+
107
179
  override connect(peerId: PeerId): void {
108
180
  this.peerId = peerId;
181
+ this._connected.wake();
109
182
  // No-op. Client always connects first
110
183
  }
111
184
 
@@ -146,18 +219,26 @@ class LocalHostNetworkAdapter extends NetworkAdapter {
146
219
  },
147
220
  });
148
221
 
149
- this.emit('peer-candidate', {
150
- peerId,
151
- });
222
+ this._connected
223
+ .wait({ timeout: 1_000 })
224
+ .then(() => {
225
+ this.emit('peer-candidate', {
226
+ peerMetadata: {},
227
+ peerId,
228
+ });
229
+ })
230
+ .catch((err) => log.catch(err));
152
231
  });
153
232
  }
154
233
 
155
234
  async sendSyncMessage({ id, syncMessage }: SyncRepoRequest): Promise<void> {
235
+ await this._connected.wait({ timeout: 1_000 });
156
236
  const message = cbor.decode(syncMessage!) as Message;
157
237
  this.emit('message', message);
158
238
  }
159
239
 
160
- getHostInfo(): HostInfo {
240
+ async getHostInfo(): Promise<HostInfo> {
241
+ await this._connected.wait({ timeout: 1_000 });
161
242
  invariant(this.peerId, 'Peer id not set.');
162
243
  return {
163
244
  peerId: this.peerId,
@@ -172,8 +253,9 @@ class LocalHostNetworkAdapter extends NetworkAdapter {
172
253
  /**
173
254
  * Used to replicate with other peers over the network.
174
255
  */
175
- class MeshNetworkAdapter extends NetworkAdapter {
256
+ export class MeshNetworkAdapter extends NetworkAdapter {
176
257
  private readonly _extensions: Map<string, AutomergeReplicator> = new Map();
258
+ private _connected = new Trigger();
177
259
 
178
260
  /**
179
261
  * Emits `ready` event. That signals to `Repo` that it can start using the adapter.
@@ -188,6 +270,7 @@ class MeshNetworkAdapter extends NetworkAdapter {
188
270
 
189
271
  override connect(peerId: PeerId): void {
190
272
  this.peerId = peerId;
273
+ this._connected.wake();
191
274
  }
192
275
 
193
276
  override send(message: Message): void {
@@ -210,20 +293,28 @@ class MeshNetworkAdapter extends NetworkAdapter {
210
293
  peerId: this.peerId,
211
294
  },
212
295
  {
213
- onStartReplication: async (info) => {
296
+ onStartReplication: async (info, remotePeerId /** Teleport ID */) => {
297
+ await this._connected.wait();
298
+
214
299
  // Note: We store only one extension per peer.
215
300
  // There can be a case where two connected peers have more than one teleport connection between them
216
301
  // and each of them uses different teleport connections to send messages.
217
302
  // It works because we receive messages from all teleport connections and Automerge Repo dedup them.
218
303
  // TODO(mykola): Use only one teleport connection per peer.
219
- if (this._extensions.has(info.id)) {
220
- return;
304
+ if (!this._extensions.has(info.id)) {
305
+ peerInfo = info;
306
+ // TODO(mykola): Fix race condition?
307
+ this._extensions.set(info.id, extension);
308
+ } else {
309
+ // TODO(mykola): retry hack.
310
+ this.emit('peer-disconnected', { peerId: info.id as PeerId });
221
311
  }
222
312
 
223
- peerInfo = info;
224
- // TODO(mykola): Fix race condition?
225
- this._extensions.set(info.id, extension);
226
313
  this.emit('peer-candidate', {
314
+ // TODO(mykola): Hack, stop abusing `peerMetadata` field.
315
+ peerMetadata: {
316
+ dxos_deviceKey: remotePeerId.toHex(),
317
+ } as any,
227
318
  peerId: info.id as PeerId,
228
319
  });
229
320
  },
@@ -233,10 +324,13 @@ class MeshNetworkAdapter extends NetworkAdapter {
233
324
  this.emit('message', message);
234
325
  },
235
326
  onClose: async () => {
236
- peerInfo &&
237
- this.emit('peer-disconnected', {
238
- peerId: peerInfo.id as PeerId,
239
- });
327
+ if (!peerInfo) {
328
+ return;
329
+ }
330
+ this.emit('peer-disconnected', {
331
+ peerId: peerInfo.id as PeerId,
332
+ });
333
+ this._extensions.delete(peerInfo.id);
240
334
  },
241
335
  },
242
336
  );
@@ -245,11 +339,18 @@ class MeshNetworkAdapter extends NetworkAdapter {
245
339
  }
246
340
 
247
341
  export class AutomergeStorageAdapter extends StorageAdapter {
342
+ // TODO(mykola): Hack for restricting automerge Repo to access storage if Host is `closed`.
343
+ // Automerge Repo do not have any lifetime management.
344
+ private _state: 'opened' | 'closed' = 'opened';
345
+
248
346
  constructor(private readonly _directory: Directory) {
249
347
  super();
250
348
  }
251
349
 
252
350
  override async load(key: StorageKey): Promise<Uint8Array | undefined> {
351
+ if (this._state !== 'opened') {
352
+ return undefined;
353
+ }
253
354
  const filename = this._getFilename(key);
254
355
  const file = this._directory.getOrCreateFile(filename);
255
356
  const { size } = await file.stat();
@@ -261,6 +362,9 @@ export class AutomergeStorageAdapter extends StorageAdapter {
261
362
  }
262
363
 
263
364
  override async save(key: StorageKey, data: Uint8Array): Promise<void> {
365
+ if (this._state !== 'opened') {
366
+ return undefined;
367
+ }
264
368
  const filename = this._getFilename(key);
265
369
  const file = this._directory.getOrCreateFile(filename);
266
370
  await file.write(0, arrayToBuffer(data));
@@ -270,13 +374,19 @@ export class AutomergeStorageAdapter extends StorageAdapter {
270
374
  }
271
375
 
272
376
  override async remove(key: StorageKey): Promise<void> {
377
+ if (this._state !== 'opened') {
378
+ return undefined;
379
+ }
273
380
  // TODO(dmaretskyi): Better deletion.
274
381
  const filename = this._getFilename(key);
275
382
  const file = this._directory.getOrCreateFile(filename);
276
- await file.truncate?.(0);
383
+ await file.destroy();
277
384
  }
278
385
 
279
386
  override async loadRange(keyPrefix: StorageKey): Promise<Chunk[]> {
387
+ if (this._state !== 'opened') {
388
+ return [];
389
+ }
280
390
  const filename = this._getFilename(keyPrefix);
281
391
  const entries = await this._directory.list();
282
392
  return Promise.all(
@@ -295,18 +405,25 @@ export class AutomergeStorageAdapter extends StorageAdapter {
295
405
  }
296
406
 
297
407
  override async removeRange(keyPrefix: StorageKey): Promise<void> {
408
+ if (this._state !== 'opened') {
409
+ return undefined;
410
+ }
298
411
  const filename = this._getFilename(keyPrefix);
299
412
  const entries = await this._directory.list();
300
413
  await Promise.all(
301
414
  entries
302
415
  .filter((entry) => entry.startsWith(filename))
303
416
  .map(async (entry): Promise<void> => {
304
- const file = this._directory.getOrCreateFile(filename);
305
- await file.truncate?.(0);
417
+ const file = this._directory.getOrCreateFile(entry);
418
+ await file.destroy();
306
419
  }),
307
420
  );
308
421
  }
309
422
 
423
+ async close(): Promise<void> {
424
+ this._state = 'closed';
425
+ }
426
+
310
427
  private _getFilename(key: StorageKey): string {
311
428
  return key.map((k) => k.replaceAll('%', '%25').replaceAll('-', '%2D')).join('-');
312
429
  }
@@ -2,4 +2,4 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- export * from './automerge-host';
5
+ export { AutomergeHost } from './automerge-host';
@@ -80,7 +80,11 @@ class Agent {
80
80
  public messages: FeedMessageBlock[] = [];
81
81
  public writePromise: Promise<any> = Promise.resolve();
82
82
 
83
- constructor(private readonly builder: TestFeedBuilder, public feedStore: FeedStore<FeedMessage>, public id: string) {}
83
+ constructor(
84
+ private readonly builder: TestFeedBuilder,
85
+ public feedStore: FeedStore<FeedMessage>,
86
+ public id: string,
87
+ ) {}
84
88
 
85
89
  async open() {
86
90
  const key = await this.builder.keyring.createKey();
@@ -131,7 +135,10 @@ type Real = {
131
135
  };
132
136
 
133
137
  class WriteCommand implements fc.AsyncCommand<Model, Real> {
134
- constructor(public agent: string, public count: number) {}
138
+ constructor(
139
+ public agent: string,
140
+ public count: number,
141
+ ) {}
135
142
 
136
143
  check = () => true;
137
144
 
@@ -216,6 +216,7 @@ export class DataPipeline implements CredentialProcessor {
216
216
  }
217
217
 
218
218
  private async _consumePipeline() {
219
+ const pipeline = this._pipeline;
219
220
  if (this.currentEpoch) {
220
221
  const waitForOneEpoch = this.onNewEpoch.waitForCount(1);
221
222
  await this._processEpochInSeparateTask(this.currentEpoch);
@@ -225,8 +226,8 @@ export class DataPipeline implements CredentialProcessor {
225
226
  // CPU bottleneck control.
226
227
  let messageCounter = 0;
227
228
 
228
- invariant(this._pipeline, 'Pipeline is not initialized.');
229
- for await (const msg of this._pipeline.consume()) {
229
+ invariant(pipeline, 'Pipeline is not initialized.');
230
+ for await (const msg of pipeline.consume()) {
230
231
  const span = this._usage.beginRecording();
231
232
  this._mutations.inc();
232
233
 
@@ -261,7 +262,7 @@ export class DataPipeline implements CredentialProcessor {
261
262
  } satisfies DataPipelineProcessed);
262
263
 
263
264
  // Timeframe clock is not updated yet.
264
- await this._noteTargetStateIfNeeded(this._pipeline.state.pendingTimeframe);
265
+ await this._noteTargetStateIfNeeded(pipeline.state.pendingTimeframe);
265
266
  }
266
267
  } catch (err: any) {
267
268
  log.catch(err);
@@ -42,7 +42,7 @@ export type ConstructSpaceParams = {
42
42
  /**
43
43
  * Called when connection auth passed successful.
44
44
  */
45
- onNetworkConnection: (session: Teleport) => void;
45
+ onAuthorizedConnection: (session: Teleport) => void;
46
46
  onAuthFailure?: (session: Teleport) => void;
47
47
  };
48
48
 
@@ -93,7 +93,7 @@ export class SpaceManager {
93
93
  async constructSpace({
94
94
  metadata,
95
95
  swarmIdentity,
96
- onNetworkConnection,
96
+ onAuthorizedConnection,
97
97
  onAuthFailure,
98
98
  memberKey,
99
99
  }: ConstructSpaceParams) {
@@ -108,7 +108,7 @@ export class SpaceManager {
108
108
  topic: spaceKey,
109
109
  swarmIdentity,
110
110
  networkManager: this._networkManager,
111
- onSessionAuth: onNetworkConnection,
111
+ onSessionAuth: onAuthorizedConnection,
112
112
  onAuthFailure,
113
113
  blobStore: this._blobStore,
114
114
  });
@@ -19,6 +19,7 @@ import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
19
19
  import { type MuxerStats, Teleport } from '@dxos/teleport';
20
20
  import { type BlobStore, BlobSync } from '@dxos/teleport-extension-object-sync';
21
21
  import { ReplicatorExtension } from '@dxos/teleport-extension-replicator';
22
+ import { trace } from '@dxos/tracing';
22
23
  import { ComplexMap } from '@dxos/util';
23
24
 
24
25
  import { AuthExtension, type AuthProvider, type AuthVerifier } from './auth';
@@ -51,6 +52,7 @@ export type SpaceProtocolOptions = {
51
52
  /**
52
53
  * Manages Teleport protocol stream creation and joining swarms with replication and presence extensions.
53
54
  */
55
+ @trace.resource()
54
56
  export class SpaceProtocol {
55
57
  private readonly _networkManager: NetworkManager;
56
58
  private readonly _swarmIdentity: SwarmIdentity;
@@ -60,8 +62,10 @@ export class SpaceProtocol {
60
62
  public readonly blobSync: BlobSync;
61
63
 
62
64
  @logInfo
65
+ @trace.info()
63
66
  private readonly _topic: Promise<PublicKey>;
64
67
 
68
+ @trace.info()
65
69
  private readonly _spaceKey: PublicKey;
66
70
 
67
71
  private readonly _feeds = new Set<FeedWrapper<FeedMessage>>();
@@ -2,7 +2,7 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import { Event, synchronized, trackLeaks, Lock } from '@dxos/async';
5
+ import { Event, synchronized, trackLeaks, Mutex } from '@dxos/async';
6
6
  import { type Context } from '@dxos/context';
7
7
  import { type FeedInfo } from '@dxos/credentials';
8
8
  import { type FeedOptions, type FeedWrapper } from '@dxos/feed-store';
@@ -52,17 +52,22 @@ export type CreatePipelineParams = {
52
52
  @trackLeaks('open', 'close')
53
53
  @trace.resource()
54
54
  export class Space {
55
- private readonly _addFeedLock = new Lock();
55
+ private readonly _addFeedMutex = new Mutex();
56
56
 
57
57
  public readonly onCredentialProcessed = new Callback<AsyncCallback<Credential>>();
58
58
  public readonly stateUpdate = new Event();
59
+ @trace.info()
59
60
  public readonly protocol: SpaceProtocol;
60
61
 
61
62
  private readonly _key: PublicKey;
62
63
  private readonly _genesisFeedKey: PublicKey;
63
64
  private readonly _feedProvider: FeedProvider;
65
+ @trace.info()
64
66
  private readonly _controlPipeline: ControlPipeline;
67
+
68
+ @trace.info()
65
69
  private readonly _dataPipeline: DataPipeline;
70
+
66
71
  private readonly _snapshotManager: SnapshotManager;
67
72
 
68
73
  private _isOpen = false;
@@ -130,7 +135,7 @@ export class Space {
130
135
  }
131
136
 
132
137
  // Add existing feeds.
133
- await this._addFeedLock.executeSynchronized(async () => {
138
+ await this._addFeedMutex.executeSynchronized(async () => {
134
139
  for (const feed of this._controlPipeline.spaceState.feeds.values()) {
135
140
  if (feed.assertion.designation === AdmittedFeed.Designation.DATA && !pipeline.hasFeed(feed.key)) {
136
141
  await pipeline.addFeed(await this._feedProvider(feed.key, { sparse: true }));
@@ -74,7 +74,10 @@ export class DatabaseTestPeer {
74
74
 
75
75
  private readonly _writes = new Set<WriteRequest>();
76
76
 
77
- constructor(public readonly rig: DatabaseTestBuilder, public readonly spaceKey: PublicKey) {}
77
+ constructor(
78
+ public readonly rig: DatabaseTestBuilder,
79
+ public readonly spaceKey: PublicKey,
80
+ ) {}
78
81
 
79
82
  async open() {
80
83
  this.hostItems = new ItemManager(this.modelFactory);
@@ -189,7 +189,7 @@ export class TestAgent {
189
189
  credentialAuthenticator: MOCK_AUTH_VERIFIER,
190
190
  },
191
191
  memberKey: identityKey,
192
- onNetworkConnection: (session) => {
192
+ onAuthorizedConnection: (session) => {
193
193
  session.addExtension(
194
194
  'dxos.mesh.teleport.gossip',
195
195
  this.createGossip().createExtension({ remotePeerId: session.remotePeerId }),