@vrtmrz/livesync-commonlib 0.1.31 → 0.1.33

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 (57) hide show
  1. package/README.md +12 -0
  2. package/dist/API/DirectFileManipulatorV2.d.ts +2 -0
  3. package/dist/API/DirectFileManipulatorV2.js +17 -5
  4. package/dist/API/processSetting.d.ts +19 -0
  5. package/dist/API/processSetting.js +91 -6
  6. package/dist/common/idDerivation.d.ts +15 -0
  7. package/dist/common/idDerivation.js +102 -0
  8. package/dist/common/models/db.definition.d.ts +1 -0
  9. package/dist/common/models/setting.const.defaults.js +3 -0
  10. package/dist/common/models/setting.const.qr.js +4 -1
  11. package/dist/common/models/setting.type.d.ts +6 -0
  12. package/dist/common/models/tweak.compatibility.js +6 -2
  13. package/dist/common/models/tweak.definition.d.ts +8 -0
  14. package/dist/common/models/tweak.definition.js +4 -3
  15. package/dist/common/settingConstants.d.ts +3 -0
  16. package/dist/common/utils.js +3 -1
  17. package/dist/events/coreEvents.d.ts +3 -0
  18. package/dist/events/coreEvents.js +2 -0
  19. package/dist/hashing.d.ts +3 -0
  20. package/dist/hashing.js +4 -0
  21. package/dist/managers/EntryManager/EntryManagerImpls.js +10 -1
  22. package/dist/managers/HashManager/HashManager.d.ts +4 -0
  23. package/dist/managers/HashManager/HashManager.js +34 -2
  24. package/dist/managers/LiveSyncManagers.js +6 -0
  25. package/dist/pouchdb/LiveSyncDBFunctions.d.ts +1 -1
  26. package/dist/pouchdb/LiveSyncDBFunctions.js +33 -38
  27. package/dist/pouchdb/negotiation.js +7 -3
  28. package/dist/pouchdb/remoteFeatureCompatibility.d.ts +10 -0
  29. package/dist/pouchdb/remoteFeatureCompatibility.js +12 -1
  30. package/dist/pouchdb/remoteIdCompatibility.d.ts +5 -0
  31. package/dist/pouchdb/remoteIdCompatibility.js +28 -0
  32. package/dist/replication/CentralCompatibility.d.ts +1 -0
  33. package/dist/replication/CentralCompatibility.js +2 -1
  34. package/dist/replication/couchdb/LiveSyncReplicator.d.ts +6 -2
  35. package/dist/replication/couchdb/LiveSyncReplicator.js +24 -5
  36. package/dist/replication/journal/LiveSyncJournalReplicator.d.ts +1 -0
  37. package/dist/replication/journal/LiveSyncJournalReplicator.js +50 -5
  38. package/dist/replication/trystero/ProxiedDB.d.ts +1 -1
  39. package/dist/replication/trystero/TrysteroReplicator.d.ts +369 -5
  40. package/dist/replication/trystero/TrysteroReplicator.js +31 -4
  41. package/dist/services/base/AppLifecycleService.d.ts +2 -0
  42. package/dist/services/base/AppLifecycleService.js +5 -0
  43. package/dist/services/base/IService.d.ts +1 -0
  44. package/dist/services/base/PathService.d.ts +3 -1
  45. package/dist/services/base/PathService.js +9 -4
  46. package/dist/services/base/SettingService.js +47 -0
  47. package/dist/settings.d.ts +2 -1
  48. package/dist/settings.js +14 -0
  49. package/dist/setupUri.d.ts +6 -0
  50. package/dist/setupUri.js +14 -0
  51. package/dist/string_and_binary/path.d.ts +1 -1
  52. package/dist/string_and_binary/path.js +3 -3
  53. package/dist/worker/bgWorker.js +1 -1
  54. package/docs/proven-in-use.md +12 -0
  55. package/docs/releasing.md +1 -1
  56. package/docs/setup-uri.md +58 -0
  57. package/package.json +17 -1
@@ -1,4 +1,4 @@
1
- import { type EntryDoc, type ObsidianLiveSyncSettings } from "../../common/types.js";
1
+ import { type EntryDoc, type ObsidianLiveSyncSettings, type P2PSyncSetting } from "../../common/types.js";
2
2
  import { type LOG_LEVEL } from "octagonal-wheels/common/logger";
3
3
  import { type ProgressInfo } from "../../pouchdb/ReplicatorShim.js";
4
4
  import type { Confirm } from "../../interfaces/Confirm.js";
@@ -115,9 +115,9 @@ export declare class TrysteroReplicator {
115
115
  private readonly fallbackAutomationCoordinator;
116
116
  server?: P2PHost;
117
117
  replicationStatus(): {};
118
- get settings(): import("../../common/types.js").P2PSyncSetting;
118
+ get settings(): P2PSyncSetting;
119
119
  /** Latest room-local policy settings supplied by the owning host. */
120
- get currentSettings(): import("../../common/types.js").P2PSyncSetting;
120
+ get currentSettings(): P2PSyncSetting;
121
121
  get db(): PouchDB.Database<EntryDoc>;
122
122
  get deviceName(): string;
123
123
  get platform(): string;
@@ -140,12 +140,376 @@ export declare class TrysteroReplicator {
140
140
  _onSetup: boolean;
141
141
  setOnSetup(): void;
142
142
  clearOnSetup(): void;
143
- getTweakSettings(fromPeerId: string): Promise<Partial<ObsidianLiveSyncSettings>>;
143
+ getTweakSettings(fromPeerId: string): Promise<{
144
+ idDerivationVersion: 0 | 1;
145
+ idDerivationProof: string;
146
+ liveSync?: boolean;
147
+ syncOnSave?: boolean;
148
+ syncOnStart?: boolean;
149
+ syncOnFileOpen?: boolean;
150
+ syncOnEditorSave?: boolean;
151
+ keepReplicationActiveInBackground?: boolean;
152
+ allowSleepDuringSynchronisation?: boolean;
153
+ allowSleepDuringSynchronisationOnDesktop?: boolean;
154
+ syncMinimumInterval?: number;
155
+ showVerboseLog?: boolean;
156
+ lessInformationInLog?: boolean;
157
+ showLongerLogInsideEditor?: boolean;
158
+ showStatusOnEditor?: boolean;
159
+ showStatusOnStatusbar?: boolean;
160
+ showOnlyIconsOnEditor?: boolean;
161
+ hideFileWarningNotice?: boolean;
162
+ networkWarningStyle?: "" | "icon" | "hidden";
163
+ displayLanguage?: import("../../common/models/setting.type.js").I18N_LANGS;
164
+ trashInsteadDelete?: boolean;
165
+ doNotDeleteFolder?: boolean;
166
+ batchSave?: boolean;
167
+ batchSaveMinimumDelay?: number;
168
+ batchSaveMaximumDelay?: number;
169
+ syncMaxSizeInMB?: number;
170
+ useIgnoreFiles?: boolean;
171
+ ignoreFiles?: string;
172
+ processSizeMismatchedFiles?: boolean;
173
+ syncOnlyRegEx?: import("../../common/types.js").CustomRegExpSourceList<"|[]|">;
174
+ syncIgnoreRegEx?: import("../../common/types.js").CustomRegExpSourceList<"|[]|">;
175
+ syncAfterMerge?: boolean;
176
+ resolveConflictsByNewerFile?: boolean;
177
+ writeDocumentsIfConflicted?: boolean;
178
+ disableMarkdownAutoMerge?: boolean;
179
+ configPassphraseStore?: import("../../common/types.js").ConfigPassphraseStore;
180
+ encryptedPassphrase?: string;
181
+ encryptedIdDerivationKey?: string;
182
+ encryptedCouchDBConnection?: string;
183
+ periodicReplication?: boolean;
184
+ periodicReplicationInterval?: number;
185
+ syncInternalFiles?: boolean;
186
+ syncInternalFilesBeforeReplication?: boolean;
187
+ syncInternalFilesInterval?: number;
188
+ syncInternalFilesIgnorePatterns?: import("../../common/types.js").CustomRegExpSourceList<",">;
189
+ syncInternalFilesTargetPatterns?: import("../../common/types.js").CustomRegExpSourceList<",">;
190
+ watchInternalFileChanges?: boolean;
191
+ suppressNotifyHiddenFilesChange?: boolean;
192
+ syncInternalFileOverwritePatterns?: import("../../common/types.js").CustomRegExpSourceList<",">;
193
+ usePluginSync?: boolean;
194
+ usePluginSettings?: boolean;
195
+ showOwnPlugins?: boolean;
196
+ autoSweepPlugins?: boolean;
197
+ autoSweepPluginsPeriodic?: boolean;
198
+ notifyPluginOrSettingUpdated?: boolean;
199
+ deviceAndVaultName?: string;
200
+ usePluginSyncV2?: boolean;
201
+ usePluginEtc?: boolean;
202
+ pluginSyncExtendedSetting?: Record<import("../../common/types.js").PluginSyncSettingEntry["key"], import("../../common/types.js").PluginSyncSettingEntry>;
203
+ useAdvancedMode?: boolean;
204
+ usePowerUserMode?: boolean;
205
+ useEdgeCaseMode?: boolean;
206
+ notifyThresholdOfRemoteStorageSize?: number;
207
+ disableWorkerForGeneratingChunks?: boolean;
208
+ processSmallFilesInUIThread?: boolean;
209
+ savingDelay?: number;
210
+ gcDelay?: number;
211
+ skipOlderFilesOnSync?: boolean;
212
+ useIndexedDBAdapter?: boolean;
213
+ enableDebugTools?: boolean;
214
+ writeLogToTheFile?: boolean;
215
+ settingSyncFile?: string;
216
+ writeCredentialsForSettingSync?: boolean;
217
+ notifyAllSettingSyncFile?: boolean;
218
+ suspendFileWatching?: boolean;
219
+ suspendParseReplicationResult?: boolean;
220
+ doNotSuspendOnFetching?: boolean;
221
+ maxMTimeForReflectEvents?: number;
222
+ versionUpFlash?: string;
223
+ settingVersion?: number;
224
+ isConfigured?: boolean;
225
+ lastReadUpdates?: number;
226
+ doctorProcessedVersion?: string;
227
+ remoteConfigurations?: Record<string, import("../../remoteConfigurations.js").RemoteConfiguration>;
228
+ activeConfigurationId?: string;
229
+ P2P_ActiveRemoteConfigurationId?: string;
230
+ couchDB_URI?: string;
231
+ couchDB_USER?: string;
232
+ couchDB_PASSWORD?: string;
233
+ couchDB_DBNAME?: string;
234
+ couchDB_CustomHeaders?: string;
235
+ useJWT?: boolean;
236
+ jwtAlgorithm?: import("../../common/models/auth.type.js").JWTAlgorithm;
237
+ jwtKey?: string;
238
+ jwtKid?: string;
239
+ jwtSub?: string;
240
+ jwtExpDuration?: number;
241
+ useRequestAPI?: boolean;
242
+ accessKey?: string;
243
+ secretKey?: string;
244
+ bucket?: string;
245
+ region?: string;
246
+ endpoint?: string;
247
+ useCustomRequestHandler?: boolean;
248
+ bucketCustomHeaders?: string;
249
+ bucketPrefix?: string;
250
+ forcePathStyle?: boolean;
251
+ remoteType?: import("../../common/types.js").RemoteType;
252
+ encrypt?: boolean;
253
+ passphrase?: string;
254
+ idDerivationKey?: string;
255
+ usePathObfuscation?: boolean;
256
+ encryptInternalMetadata?: boolean;
257
+ E2EEAlgorithm?: import("../../common/types.js").E2EEAlgorithm;
258
+ hashAlg?: import("../../common/types.js").HashAlgorithm;
259
+ minimumChunkSize?: number;
260
+ customChunkSize?: number;
261
+ longLineThreshold?: number;
262
+ useSegmenter?: boolean;
263
+ enableChunkSplitterV2?: boolean;
264
+ doNotUseFixedRevisionForChunks?: boolean;
265
+ chunkSplitterVersion?: import("../../common/types.js").ChunkSplitterVersion;
266
+ useEden?: boolean;
267
+ maxChunksInEden?: number;
268
+ maxTotalLengthInEden?: number;
269
+ maxAgeInEden?: number;
270
+ tweakModified?: number | undefined;
271
+ checkIntegrityOnSave?: boolean;
272
+ useHistory?: boolean;
273
+ disableRequestURI?: boolean;
274
+ sendChunksBulk?: boolean;
275
+ sendChunksBulkMaxSize?: number;
276
+ useDynamicIterationCount?: boolean;
277
+ doNotPaceReplication?: boolean;
278
+ readChunksOnline?: boolean;
279
+ useOnlyLocalChunk?: boolean;
280
+ concurrencyOfReadChunksOnline?: number;
281
+ minimumIntervalOfReadChunksOnline?: number;
282
+ enableCompression?: boolean;
283
+ batch_size?: number;
284
+ batches_limit?: number;
285
+ ignoreVersionCheck?: boolean;
286
+ disableCheckingConfigMismatch?: boolean;
287
+ autoAcceptCompatibleTweak?: boolean | undefined;
288
+ hashCacheMaxCount?: number;
289
+ hashCacheMaxAmount?: number;
290
+ permitEmptyPassphrase?: boolean;
291
+ handleFilenameCaseSensitive?: boolean;
292
+ checkConflictOnlyOnOpen?: boolean;
293
+ showMergeDialogOnlyOnActive?: boolean;
294
+ additionalSuffixOfDatabaseName?: string | undefined;
295
+ useTimeouts?: boolean;
296
+ deleteMetadataOfDeletedFiles?: boolean;
297
+ automaticallyDeleteMetadataOfDeletedFiles?: number;
298
+ P2P_AutoAccepting?: import("../../common/types.js").AutoAccepting;
299
+ P2P_AutoSyncPeers?: string;
300
+ P2P_AutoWatchPeers?: string;
301
+ P2P_SyncOnReplication?: string;
302
+ P2P_RebuildFrom?: string;
303
+ P2P_AutoAcceptingPeers?: string;
304
+ P2P_AutoDenyingPeers?: string;
305
+ P2P_IsHeadless?: boolean;
306
+ P2P_iceServers?: readonly RTCIceServer[];
307
+ P2P_iceServersExpiresAt?: number;
308
+ P2P_Enabled?: boolean;
309
+ P2P_relays?: string;
310
+ P2P_roomID?: string;
311
+ P2P_passphrase?: string;
312
+ P2P_AppID?: string;
313
+ P2P_AutoStart?: boolean;
314
+ P2P_AutoBroadcast?: boolean;
315
+ P2P_DevicePeerName?: string;
316
+ P2P_turnServers?: string;
317
+ P2P_turnUsername?: string;
318
+ P2P_turnCredential?: string;
319
+ P2P_managedType?: string;
320
+ P2P_managedId?: string;
321
+ P2P_managedToken?: string;
322
+ P2P_maxWirePayloadBytes?: number;
323
+ P2P_connectionPath?: import("../../common/types.js").P2PConnectionPath;
324
+ P2P_useDiagRTC?: boolean;
325
+ }>;
144
326
  private handleSynchronisationRequest;
145
327
  getCommands(): {
146
328
  reqSync: (fromPeerId: string) => Promise<P2PReplicationWireResult>;
147
329
  "!reqAuth": (fromPeerId: string) => Promise<boolean>;
148
- getTweakSettings: (fromPeerId: string) => Promise<Partial<ObsidianLiveSyncSettings>>;
330
+ getTweakSettings: (fromPeerId: string) => Promise<{
331
+ idDerivationVersion: 0 | 1;
332
+ idDerivationProof: string;
333
+ liveSync?: boolean;
334
+ syncOnSave?: boolean;
335
+ syncOnStart?: boolean;
336
+ syncOnFileOpen?: boolean;
337
+ syncOnEditorSave?: boolean;
338
+ keepReplicationActiveInBackground?: boolean;
339
+ allowSleepDuringSynchronisation?: boolean;
340
+ allowSleepDuringSynchronisationOnDesktop?: boolean;
341
+ syncMinimumInterval?: number;
342
+ showVerboseLog?: boolean;
343
+ lessInformationInLog?: boolean;
344
+ showLongerLogInsideEditor?: boolean;
345
+ showStatusOnEditor?: boolean;
346
+ showStatusOnStatusbar?: boolean;
347
+ showOnlyIconsOnEditor?: boolean;
348
+ hideFileWarningNotice?: boolean;
349
+ networkWarningStyle?: "" | "icon" | "hidden";
350
+ displayLanguage?: import("../../common/models/setting.type.js").I18N_LANGS;
351
+ trashInsteadDelete?: boolean;
352
+ doNotDeleteFolder?: boolean;
353
+ batchSave?: boolean;
354
+ batchSaveMinimumDelay?: number;
355
+ batchSaveMaximumDelay?: number;
356
+ syncMaxSizeInMB?: number;
357
+ useIgnoreFiles?: boolean;
358
+ ignoreFiles?: string;
359
+ processSizeMismatchedFiles?: boolean;
360
+ syncOnlyRegEx?: import("../../common/types.js").CustomRegExpSourceList<"|[]|">;
361
+ syncIgnoreRegEx?: import("../../common/types.js").CustomRegExpSourceList<"|[]|">;
362
+ syncAfterMerge?: boolean;
363
+ resolveConflictsByNewerFile?: boolean;
364
+ writeDocumentsIfConflicted?: boolean;
365
+ disableMarkdownAutoMerge?: boolean;
366
+ configPassphraseStore?: import("../../common/types.js").ConfigPassphraseStore;
367
+ encryptedPassphrase?: string;
368
+ encryptedIdDerivationKey?: string;
369
+ encryptedCouchDBConnection?: string;
370
+ periodicReplication?: boolean;
371
+ periodicReplicationInterval?: number;
372
+ syncInternalFiles?: boolean;
373
+ syncInternalFilesBeforeReplication?: boolean;
374
+ syncInternalFilesInterval?: number;
375
+ syncInternalFilesIgnorePatterns?: import("../../common/types.js").CustomRegExpSourceList<",">;
376
+ syncInternalFilesTargetPatterns?: import("../../common/types.js").CustomRegExpSourceList<",">;
377
+ watchInternalFileChanges?: boolean;
378
+ suppressNotifyHiddenFilesChange?: boolean;
379
+ syncInternalFileOverwritePatterns?: import("../../common/types.js").CustomRegExpSourceList<",">;
380
+ usePluginSync?: boolean;
381
+ usePluginSettings?: boolean;
382
+ showOwnPlugins?: boolean;
383
+ autoSweepPlugins?: boolean;
384
+ autoSweepPluginsPeriodic?: boolean;
385
+ notifyPluginOrSettingUpdated?: boolean;
386
+ deviceAndVaultName?: string;
387
+ usePluginSyncV2?: boolean;
388
+ usePluginEtc?: boolean;
389
+ pluginSyncExtendedSetting?: Record<import("../../common/types.js").PluginSyncSettingEntry["key"], import("../../common/types.js").PluginSyncSettingEntry>;
390
+ useAdvancedMode?: boolean;
391
+ usePowerUserMode?: boolean;
392
+ useEdgeCaseMode?: boolean;
393
+ notifyThresholdOfRemoteStorageSize?: number;
394
+ disableWorkerForGeneratingChunks?: boolean;
395
+ processSmallFilesInUIThread?: boolean;
396
+ savingDelay?: number;
397
+ gcDelay?: number;
398
+ skipOlderFilesOnSync?: boolean;
399
+ useIndexedDBAdapter?: boolean;
400
+ enableDebugTools?: boolean;
401
+ writeLogToTheFile?: boolean;
402
+ settingSyncFile?: string;
403
+ writeCredentialsForSettingSync?: boolean;
404
+ notifyAllSettingSyncFile?: boolean;
405
+ suspendFileWatching?: boolean;
406
+ suspendParseReplicationResult?: boolean;
407
+ doNotSuspendOnFetching?: boolean;
408
+ maxMTimeForReflectEvents?: number;
409
+ versionUpFlash?: string;
410
+ settingVersion?: number;
411
+ isConfigured?: boolean;
412
+ lastReadUpdates?: number;
413
+ doctorProcessedVersion?: string;
414
+ remoteConfigurations?: Record<string, import("../../remoteConfigurations.js").RemoteConfiguration>;
415
+ activeConfigurationId?: string;
416
+ P2P_ActiveRemoteConfigurationId?: string;
417
+ couchDB_URI?: string;
418
+ couchDB_USER?: string;
419
+ couchDB_PASSWORD?: string;
420
+ couchDB_DBNAME?: string;
421
+ couchDB_CustomHeaders?: string;
422
+ useJWT?: boolean;
423
+ jwtAlgorithm?: import("../../common/models/auth.type.js").JWTAlgorithm;
424
+ jwtKey?: string;
425
+ jwtKid?: string;
426
+ jwtSub?: string;
427
+ jwtExpDuration?: number;
428
+ useRequestAPI?: boolean;
429
+ accessKey?: string;
430
+ secretKey?: string;
431
+ bucket?: string;
432
+ region?: string;
433
+ endpoint?: string;
434
+ useCustomRequestHandler?: boolean;
435
+ bucketCustomHeaders?: string;
436
+ bucketPrefix?: string;
437
+ forcePathStyle?: boolean;
438
+ remoteType?: import("../../common/types.js").RemoteType;
439
+ encrypt?: boolean;
440
+ passphrase?: string;
441
+ idDerivationKey?: string;
442
+ usePathObfuscation?: boolean;
443
+ encryptInternalMetadata?: boolean;
444
+ E2EEAlgorithm?: import("../../common/types.js").E2EEAlgorithm;
445
+ hashAlg?: import("../../common/types.js").HashAlgorithm;
446
+ minimumChunkSize?: number;
447
+ customChunkSize?: number;
448
+ longLineThreshold?: number;
449
+ useSegmenter?: boolean;
450
+ enableChunkSplitterV2?: boolean;
451
+ doNotUseFixedRevisionForChunks?: boolean;
452
+ chunkSplitterVersion?: import("../../common/types.js").ChunkSplitterVersion;
453
+ useEden?: boolean;
454
+ maxChunksInEden?: number;
455
+ maxTotalLengthInEden?: number;
456
+ maxAgeInEden?: number;
457
+ tweakModified?: number | undefined;
458
+ checkIntegrityOnSave?: boolean;
459
+ useHistory?: boolean;
460
+ disableRequestURI?: boolean;
461
+ sendChunksBulk?: boolean;
462
+ sendChunksBulkMaxSize?: number;
463
+ useDynamicIterationCount?: boolean;
464
+ doNotPaceReplication?: boolean;
465
+ readChunksOnline?: boolean;
466
+ useOnlyLocalChunk?: boolean;
467
+ concurrencyOfReadChunksOnline?: number;
468
+ minimumIntervalOfReadChunksOnline?: number;
469
+ enableCompression?: boolean;
470
+ batch_size?: number;
471
+ batches_limit?: number;
472
+ ignoreVersionCheck?: boolean;
473
+ disableCheckingConfigMismatch?: boolean;
474
+ autoAcceptCompatibleTweak?: boolean | undefined;
475
+ hashCacheMaxCount?: number;
476
+ hashCacheMaxAmount?: number;
477
+ permitEmptyPassphrase?: boolean;
478
+ handleFilenameCaseSensitive?: boolean;
479
+ checkConflictOnlyOnOpen?: boolean;
480
+ showMergeDialogOnlyOnActive?: boolean;
481
+ additionalSuffixOfDatabaseName?: string | undefined;
482
+ useTimeouts?: boolean;
483
+ deleteMetadataOfDeletedFiles?: boolean;
484
+ automaticallyDeleteMetadataOfDeletedFiles?: number;
485
+ P2P_AutoAccepting?: import("../../common/types.js").AutoAccepting;
486
+ P2P_AutoSyncPeers?: string;
487
+ P2P_AutoWatchPeers?: string;
488
+ P2P_SyncOnReplication?: string;
489
+ P2P_RebuildFrom?: string;
490
+ P2P_AutoAcceptingPeers?: string;
491
+ P2P_AutoDenyingPeers?: string;
492
+ P2P_IsHeadless?: boolean;
493
+ P2P_iceServers?: readonly RTCIceServer[];
494
+ P2P_iceServersExpiresAt?: number;
495
+ P2P_Enabled?: boolean;
496
+ P2P_relays?: string;
497
+ P2P_roomID?: string;
498
+ P2P_passphrase?: string;
499
+ P2P_AppID?: string;
500
+ P2P_AutoStart?: boolean;
501
+ P2P_AutoBroadcast?: boolean;
502
+ P2P_DevicePeerName?: string;
503
+ P2P_turnServers?: string;
504
+ P2P_turnUsername?: string;
505
+ P2P_turnCredential?: string;
506
+ P2P_managedType?: string;
507
+ P2P_managedId?: string;
508
+ P2P_managedToken?: string;
509
+ P2P_maxWirePayloadBytes?: number;
510
+ P2P_connectionPath?: import("../../common/types.js").P2PConnectionPath;
511
+ P2P_useDiagRTC?: boolean;
512
+ }>;
149
513
  onProgress: (fromPeerId: string) => Promise<{
150
514
  error: import("../../rpc/index.js").RpcErrorShape;
151
515
  }>;
@@ -24,6 +24,9 @@ import { sha1 } from "octagonal-wheels/hash/purejs";
24
24
  import { getRelaySockets, pauseRelayReconnection, resumeRelayReconnection } from "@trystero-p2p/nostr";
25
25
  import { P2PAutomationCoordinator } from "./P2PAutomationCoordinator.js";
26
26
  import { compatGlobal } from "../../common/coreEnvFunctions.js";
27
+ import { computeKeyedId, configuredIdKey } from "../../common/idDerivation.js";
28
+ import { getWebCrypto } from "../../mods.js";
29
+ import { uint8ArrayToHexString } from "../../string_and_binary/convert.js";
27
30
  import {
28
31
  fromP2PReplicationWireResult,
29
32
  toP2PReplicationWireResult,
@@ -216,7 +219,14 @@ class TrysteroReplicator {
216
219
  delete allSettings[key];
217
220
  }
218
221
  }
219
- return allSettings;
222
+ const idSettings = this.currentSettings;
223
+ const idDerivationVersion = idSettings.idDerivationVersion ?? 0;
224
+ const idKey = configuredIdKey({ idDerivationVersion, idDerivationKey: idSettings.idDerivationKey ?? "" });
225
+ return {
226
+ ...allSettings,
227
+ idDerivationVersion,
228
+ idDerivationProof: idKey && idSettings.encrypt && idSettings.usePathObfuscation ? await computeKeyedId(idKey, "peer-agreement", fromPeerId) : ""
229
+ };
220
230
  }
221
231
  async handleSynchronisationRequest(fromPeerId, signal) {
222
232
  if (this._onSetup) {
@@ -706,7 +716,13 @@ ${info.lastSeq} / ${info.maxSeqInBatch})`,
706
716
  const decryptedConfig = JSON.parse(
707
717
  await decrypt(encryptedConfig, passphrase)
708
718
  );
709
- return omitP2PRuntimeSettings(decryptedConfig);
719
+ const settings = {
720
+ ...decryptedConfig,
721
+ idDerivationVersion: decryptedConfig.idDerivationVersion ?? 0,
722
+ idDerivationKey: decryptedConfig.idDerivationKey ?? ""
723
+ };
724
+ configuredIdKey(settings);
725
+ return omitP2PRuntimeSettings(settings);
710
726
  } catch (e) {
711
727
  Logger("Error while decrypting the configuration", LOG_LEVEL_NOTICE);
712
728
  Logger(e, LOG_LEVEL_VERBOSE);
@@ -731,9 +747,12 @@ ${info.lastSeq} / ${info.maxSeqInBatch})`,
731
747
  return true;
732
748
  }
733
749
  const connection = this.server.getConnection(peerId);
734
- const tweakValues = await connection.invokeRemoteObjectFunction("getTweakSettings", [this.server.serverPeerId], 5e3, signal);
750
+ const challengeBytes = new Uint8Array(16);
751
+ (await getWebCrypto()).getRandomValues(challengeBytes);
752
+ const challenge = uint8ArrayToHexString(challengeBytes);
753
+ const tweakValues = await connection.invokeRemoteObjectFunction("getTweakSettings", [challenge], 5e3, signal);
735
754
  if (signal?.aborted) return false;
736
- const thisTweakValues = await this.getTweakSettings("");
755
+ const thisTweakValues = await this.getTweakSettings(challenge);
737
756
  if (thisTweakValues.passphrase !== tweakValues.passphrase) {
738
757
  Logger(
739
758
  "Replication cancelled: Passphrase is not matched\nCannot replicate to a remote database until the problem is resolved.",
@@ -741,6 +760,14 @@ ${info.lastSeq} / ${info.maxSeqInBatch})`,
741
760
  );
742
761
  return false;
743
762
  }
763
+ if (thisTweakValues.encrypt !== tweakValues.encrypt || thisTweakValues.usePathObfuscation !== tweakValues.usePathObfuscation) {
764
+ Logger("Replication cancelled: The encryption or path obfuscation settings do not match this peer.", logLevel);
765
+ return false;
766
+ }
767
+ if (thisTweakValues.encrypt && thisTweakValues.usePathObfuscation && ((thisTweakValues.idDerivationVersion ?? 0) !== (tweakValues.idDerivationVersion ?? 0) || (thisTweakValues.idDerivationProof ?? "") !== (tweakValues.idDerivationProof ?? ""))) {
768
+ Logger("Replication cancelled: The ID derivation settings do not match this peer.", logLevel);
769
+ return false;
770
+ }
744
771
  const tweakAssessment = assessTweakCompatibility(thisTweakValues, tweakValues);
745
772
  if (!tweakAssessment.representationDiffers) return true;
746
773
  Logger(
@@ -119,6 +119,8 @@ export declare abstract class AppLifecycleService<T extends ServiceContext = Ser
119
119
  isReady(): boolean;
120
120
  /**
121
121
  * Mark the plug-in as ready.
122
+ * Establishing readiness emits `EVENT_APPLICATION_READY` on the context's event channel, so a host can
123
+ * continue work it held while the plug-in was not ready. Marking a plug-in which is already ready emits nothing.
122
124
  */
123
125
  markIsReady(): void;
124
126
  /**
@@ -1,3 +1,4 @@
1
+ import { EVENT_APPLICATION_READY } from "../../events/coreEvents.js";
1
2
  import { handlers } from "../lib/HandlerUtils.js";
2
3
  import { ServiceBase } from "./ServiceBase.js";
3
4
  class AppLifecycleService extends ServiceBase {
@@ -107,9 +108,13 @@ class AppLifecycleService extends ServiceBase {
107
108
  }
108
109
  /**
109
110
  * Mark the plug-in as ready.
111
+ * Establishing readiness emits `EVENT_APPLICATION_READY` on the context's event channel, so a host can
112
+ * continue work it held while the plug-in was not ready. Marking a plug-in which is already ready emits nothing.
110
113
  */
111
114
  markIsReady() {
115
+ if (this._isReady) return;
112
116
  this._isReady = true;
117
+ this.context.events.emitEvent(EVENT_APPLICATION_READY);
113
118
  }
114
119
  /**
115
120
  * Reset the ready state of the plug-in.
@@ -60,6 +60,7 @@ export interface IAPIService {
60
60
  export interface IPathService {
61
61
  id2path(id: DocumentID, entry?: EntryHasPath, stripPrefix?: boolean): FilePathWithPrefix;
62
62
  path2id(filename: FilePathWithPrefix | FilePath, prefix?: string): Promise<DocumentID>;
63
+ path2idWithSettings(filename: FilePathWithPrefix | FilePath, setting: Pick<RemoteDBSettings, "encrypt" | "usePathObfuscation" | "passphrase" | "handleFilenameCaseSensitive" | "idDerivationVersion" | "idDerivationKey">, prefix?: string): Promise<DocumentID>;
63
64
  getPath(entry: AnyEntry): FilePathWithPrefix;
64
65
  markChangesAreSame(old: UXFileInfo | AnyEntry | FilePathWithPrefix, newMtime: number, oldMtime: number): boolean | undefined;
65
66
  unmarkChanges(file: AnyEntry | FilePathWithPrefix | UXFileInfoStub): void;
@@ -1,4 +1,4 @@
1
- import type { DocumentID, EntryHasPath, FilePathWithPrefix, FilePath, AnyEntry, UXFileInfo, UXFileInfoStub } from "../../common/types.js";
1
+ import type { DocumentID, EntryHasPath, FilePathWithPrefix, FilePath, AnyEntry, RemoteDBSettings, UXFileInfo, UXFileInfoStub } from "../../common/types.js";
2
2
  import type { IPathService, ISettingService } from "./IService.js";
3
3
  import { ServiceBase, type ServiceContext } from "./ServiceBase.js";
4
4
  import type { BASE_IS_NEW, EVEN, TARGET_IS_NEW } from "../../common/models/shared.const.symbols.js";
@@ -36,6 +36,8 @@ export declare abstract class PathService<T extends ServiceContext = ServiceCont
36
36
  * @param prefix The prefix to use for the document ID.
37
37
  */
38
38
  path2id(filename: FilePathWithPrefix | FilePath, prefix?: string): Promise<DocumentID>;
39
+ /** Convert a path using the caller's settings snapshot and this host's path normalisation. */
40
+ path2idWithSettings(filename: FilePathWithPrefix | FilePath, setting: Pick<RemoteDBSettings, "encrypt" | "usePathObfuscation" | "passphrase" | "handleFilenameCaseSensitive" | "idDerivationVersion" | "idDerivationKey">, prefix?: string): Promise<DocumentID>;
39
41
  getPath(entry: AnyEntry): FilePathWithPrefix;
40
42
  abstract markChangesAreSame(old: UXFileInfo | AnyEntry | FilePathWithPrefix, newMtime: number, oldMtime: number): boolean | undefined;
41
43
  abstract unmarkChanges(file: AnyEntry | FilePathWithPrefix | UXFileInfoStub): void;
@@ -1,5 +1,6 @@
1
1
  import { ServiceBase } from "./ServiceBase.js";
2
2
  import { addPrefix, expandFilePathPrefix, id2path_base, path2id_base } from "../../string_and_binary/path.js";
3
+ import { configuredIdKey } from "../../common/idDerivation.js";
3
4
  import { isInternalMetadata, stripInternalMetadataPrefix } from "../../common/typeUtils.js";
4
5
  class PathService extends ServiceBase {
5
6
  settingService;
@@ -17,10 +18,10 @@ class PathService extends ServiceBase {
17
18
  const [prefix, path] = expandFilePathPrefix(filename);
18
19
  return prefix + this.normalizePath(path);
19
20
  }
20
- async _path2id(filename, obfuscatePassphrase, caseInsensitive) {
21
+ async _path2id(filename, obfuscatePassphrase, caseInsensitive, idDerivationKey) {
21
22
  const [prefix, path] = expandFilePathPrefix(filename);
22
23
  const fixedPath = prefix + this.normalizePath(path);
23
- const out = await path2id_base(fixedPath, obfuscatePassphrase, caseInsensitive);
24
+ const out = await path2id_base(fixedPath, obfuscatePassphrase, caseInsensitive, idDerivationKey);
24
25
  return out;
25
26
  }
26
27
  /**
@@ -44,13 +45,17 @@ class PathService extends ServiceBase {
44
45
  * @param prefix The prefix to use for the document ID.
45
46
  */
46
47
  async path2id(filename, prefix) {
48
+ return this.path2idWithSettings(filename, this.settings, prefix);
49
+ }
50
+ /** Convert a path using the caller's settings snapshot and this host's path normalisation. */
51
+ async path2idWithSettings(filename, setting, prefix) {
47
52
  const destPath = addPrefix(filename, prefix ?? "");
48
- const setting = this.settings;
49
53
  const pathObfuscationPassphrase = this.getPathObfuscationPassphrase?.() ?? (setting.usePathObfuscation ? setting.passphrase : false);
50
54
  return await this._path2id(
51
55
  destPath,
52
56
  pathObfuscationPassphrase,
53
- !setting.handleFilenameCaseSensitive
57
+ !setting.handleFilenameCaseSensitive,
58
+ setting.encrypt && configuredIdKey(setting) || void 0
54
59
  );
55
60
  }
56
61
  getPath(entry) {
@@ -21,6 +21,7 @@ import {
21
21
  migrateP2PActiveRemoteConfigurationIdInPlace
22
22
  } from "../../serviceFeatures/remoteConfig.js";
23
23
  import { ConnectionStringParser } from "../../common/ConnectionString.js";
24
+ import { configuredIdKey } from "../../common/idDerivation.js";
24
25
  class SettingService extends ServiceBase {
25
26
  deviceAndVaultName = "";
26
27
  APIService;
@@ -152,6 +153,32 @@ class SettingService extends ServiceBase {
152
153
  Object.assign(settings, patch);
153
154
  Object.assign(this.settings, patch);
154
155
  }
156
+ const idKey = configuredIdKey(settings);
157
+ if (settings.idDerivationVersion === 1) {
158
+ if (idKey === false) {
159
+ throw new Error("The configured ID derivation key is unavailable.");
160
+ }
161
+ let passphrase = this.usedPassphrase;
162
+ if (passphrase === "") {
163
+ passphrase = await this.getPassphrase(settings);
164
+ }
165
+ if (passphrase === false || passphrase === "") {
166
+ const message = "Failed to retrieve a passphrase for the ID derivation key. Settings were not saved.";
167
+ this._log(message, LOG_LEVEL_URGENT);
168
+ throw new Error(message);
169
+ }
170
+ const encryptedIdKey = await encryptString(idKey, passphrase + SALT_OF_PASSPHRASE);
171
+ if (encryptedIdKey === "") {
172
+ const message = "Failed to encrypt the ID derivation key. Settings were not saved.";
173
+ this._log(message, LOG_LEVEL_URGENT);
174
+ throw new Error(message);
175
+ }
176
+ settings.encryptedIdDerivationKey = encryptedIdKey;
177
+ settings.idDerivationKey = "";
178
+ this.usedPassphrase = passphrase;
179
+ } else {
180
+ settings.encryptedIdDerivationKey = "";
181
+ }
155
182
  settings.deviceAndVaultName = "";
156
183
  if (settings.P2P_DevicePeerName && settings.P2P_DevicePeerName.trim() !== "") {
157
184
  this._log("Saving device peer name to small config");
@@ -435,7 +462,27 @@ class SettingService extends ServiceBase {
435
462
  * @param settings The settings to decrypt.
436
463
  */
437
464
  async decryptSettings(settings) {
465
+ if (settings.idDerivationVersion !== 1) {
466
+ configuredIdKey(settings);
467
+ } else if (!settings.encryptedIdDerivationKey) {
468
+ throw new Error("The configured ID derivation key is missing or unavailable.");
469
+ }
438
470
  const passphrase = await this.getPassphrase(settings);
471
+ if (settings.idDerivationVersion === 1) {
472
+ if (passphrase === false || passphrase === "") {
473
+ throw new Error("The configured ID derivation key cannot be decrypted without a passphrase.");
474
+ }
475
+ const decryptedIdKey = await this.decryptConfigurationItem(settings.encryptedIdDerivationKey, passphrase);
476
+ if (decryptedIdKey === false) {
477
+ throw new Error("The configured ID derivation key could not be decrypted.");
478
+ }
479
+ try {
480
+ configuredIdKey({ ...settings, idDerivationKey: decryptedIdKey });
481
+ } catch {
482
+ throw new Error("The configured ID derivation key is invalid.");
483
+ }
484
+ settings.idDerivationKey = decryptedIdKey;
485
+ }
439
486
  if (passphrase === false) {
440
487
  this._log("No passphrase found for data.json! Verify configuration before syncing.", LOG_LEVEL_URGENT);
441
488
  const hasEncryptedRemoteConfigurations = Object.values(settings.remoteConfigurations || {}).some(
@@ -6,8 +6,9 @@
6
6
  export { DEFAULT_SETTINGS, NEW_VAULT_SETTINGS, P2P_DEFAULT_SETTINGS, SETTINGS_SCHEMA_DEFAULTS, createNewVaultSettings, } from "./common/models/setting.const.defaults.js";
7
7
  export { PREFERRED_BASE, PREFERRED_JOURNAL_SYNC, PREFERRED_SETTING_CLOUDANT, PREFERRED_SETTING_SELF_HOSTED, } from "./common/models/setting.const.preferred.js";
8
8
  export { CURRENT_SETTING_VERSION } from "./common/models/setting.const.js";
9
+ export { deriveIdKey, deriveOrImportIdKey, formatIdRecoveryCode, configuredIdKey, ID_DERIVATION_VERSION, ID_RECOVERY_CODE_PREFIX, } from "./common/idDerivation.js";
9
10
  export { prepareSettingsForLoad, SettingsMigrationReviewCodes, type PreparedSettings, type SettingsMigrationReviewCode, type SettingsMigrationReviewReason, type SettingsMigrationState, } from "./common/models/setting.lifecycle.js";
10
11
  export { assessTweakCompatibility } from "./common/models/tweak.compatibility.js";
11
12
  export type { TweakAssessment, TweakAssessmentEntry, TweakAssessmentRelation, TweakAssessmentValue, TweakTransition, TweakTransitionReason, } from "./common/models/tweak.compatibility.js";
12
13
  export type { TweakReconstruction, TweakValues } from "./common/models/tweak.definition.js";
13
- export type { ObsidianLiveSyncSettings, RemoteDBSettings, RemoteTypeSettings, } from "./common/models/setting.type.js";
14
+ export type { ObsidianLiveSyncSettings, RemoteDBSettings, RemoteTypeSettings } from "./common/models/setting.type.js";