@vrtmrz/livesync-commonlib 0.1.30 → 0.1.32
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/README.md +5 -0
- package/dist/API/DirectFileManipulatorV2.d.ts +2 -0
- package/dist/API/DirectFileManipulatorV2.js +17 -5
- package/dist/API/processSetting.d.ts +19 -0
- package/dist/API/processSetting.js +91 -6
- package/dist/common/idDerivation.d.ts +13 -0
- package/dist/common/idDerivation.js +75 -0
- package/dist/common/models/db.definition.d.ts +1 -0
- package/dist/common/models/setting.const.defaults.js +3 -0
- package/dist/common/models/setting.const.qr.js +4 -1
- package/dist/common/models/setting.type.d.ts +6 -0
- package/dist/common/models/tweak.compatibility.js +6 -2
- package/dist/common/models/tweak.definition.d.ts +8 -0
- package/dist/common/models/tweak.definition.js +4 -3
- package/dist/common/settingConstants.d.ts +3 -0
- package/dist/common/utils.js +3 -1
- package/dist/events/coreEvents.d.ts +3 -0
- package/dist/events/coreEvents.js +2 -0
- package/dist/managers/EntryManager/EntryManagerImpls.js +10 -1
- package/dist/managers/HashManager/HashManager.d.ts +1 -0
- package/dist/managers/HashManager/HashManager.js +13 -1
- package/dist/pouchdb/LiveSyncDBFunctions.d.ts +1 -1
- package/dist/pouchdb/LiveSyncDBFunctions.js +33 -38
- package/dist/pouchdb/negotiation.js +7 -3
- package/dist/pouchdb/remoteFeatureCompatibility.d.ts +10 -0
- package/dist/pouchdb/remoteFeatureCompatibility.js +12 -1
- package/dist/pouchdb/remoteIdCompatibility.d.ts +5 -0
- package/dist/pouchdb/remoteIdCompatibility.js +28 -0
- package/dist/replication/CentralCompatibility.d.ts +1 -0
- package/dist/replication/CentralCompatibility.js +2 -1
- package/dist/replication/couchdb/LiveSyncReplicator.d.ts +6 -2
- package/dist/replication/couchdb/LiveSyncReplicator.js +24 -5
- package/dist/replication/journal/LiveSyncJournalReplicator.d.ts +1 -0
- package/dist/replication/journal/LiveSyncJournalReplicator.js +50 -5
- package/dist/replication/trystero/TrysteroReplicator.d.ts +369 -5
- package/dist/replication/trystero/TrysteroReplicator.js +31 -4
- package/dist/services/base/AppLifecycleService.d.ts +2 -0
- package/dist/services/base/AppLifecycleService.js +5 -0
- package/dist/services/base/IService.d.ts +1 -0
- package/dist/services/base/PathService.d.ts +3 -1
- package/dist/services/base/PathService.js +9 -4
- package/dist/services/base/SettingService.d.ts +2 -0
- package/dist/services/base/SettingService.js +57 -3
- package/dist/settings.d.ts +2 -1
- package/dist/settings.js +14 -0
- package/dist/setupUri.d.ts +6 -0
- package/dist/setupUri.js +14 -0
- package/dist/string_and_binary/path.d.ts +1 -1
- package/dist/string_and_binary/path.js +3 -3
- package/dist/worker/bgWorker.js +1 -1
- package/docs/proven-in-use.md +8 -0
- package/docs/releasing.md +1 -1
- package/docs/setup-uri.md +58 -0
- package/package.json +9 -1
|
@@ -13,6 +13,7 @@ import { ServiceBase } from "./ServiceBase.js";
|
|
|
13
13
|
import { createInstanceLogFunction } from "../lib/logUtils.js";
|
|
14
14
|
import { isCloudantURI } from "../../pouchdb/utils_couchdb.js";
|
|
15
15
|
import { decryptString, encryptString } from "../../encryption/stringEncryption.js";
|
|
16
|
+
import { encryptWithEphemeralSalt } from "octagonal-wheels/encryption/hkdf";
|
|
16
17
|
import {
|
|
17
18
|
activateP2PRemoteConfiguration,
|
|
18
19
|
activateRemoteConfiguration,
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
migrateP2PActiveRemoteConfigurationIdInPlace
|
|
21
22
|
} from "../../serviceFeatures/remoteConfig.js";
|
|
22
23
|
import { ConnectionStringParser } from "../../common/ConnectionString.js";
|
|
24
|
+
import { configuredIdKey } from "../../common/idDerivation.js";
|
|
23
25
|
class SettingService extends ServiceBase {
|
|
24
26
|
deviceAndVaultName = "";
|
|
25
27
|
APIService;
|
|
@@ -151,6 +153,32 @@ class SettingService extends ServiceBase {
|
|
|
151
153
|
Object.assign(settings, patch);
|
|
152
154
|
Object.assign(this.settings, patch);
|
|
153
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
|
+
}
|
|
154
182
|
settings.deviceAndVaultName = "";
|
|
155
183
|
if (settings.P2P_DevicePeerName && settings.P2P_DevicePeerName.trim() !== "") {
|
|
156
184
|
this._log("Saving device peer name to small config");
|
|
@@ -207,7 +235,7 @@ class SettingService extends ServiceBase {
|
|
|
207
235
|
settings.endpoint = "";
|
|
208
236
|
}
|
|
209
237
|
if (settings.encrypt && settings.passphrase != "") {
|
|
210
|
-
settings.encryptedPassphrase = await this.
|
|
238
|
+
settings.encryptedPassphrase = await this.encryptPlainConfigurationItem(settings.passphrase, settings);
|
|
211
239
|
settings.passphrase = "";
|
|
212
240
|
}
|
|
213
241
|
await this.encryptRemoteConfigurationUris(settings);
|
|
@@ -405,8 +433,14 @@ class SettingService extends ServiceBase {
|
|
|
405
433
|
return false;
|
|
406
434
|
}
|
|
407
435
|
async encryptConfigurationItem(src, settings) {
|
|
436
|
+
return this.encryptConfigurationItemWith(src, settings, encryptString);
|
|
437
|
+
}
|
|
438
|
+
async encryptPlainConfigurationItem(src, settings) {
|
|
439
|
+
return this.encryptConfigurationItemWith(src, settings, encryptWithEphemeralSalt);
|
|
440
|
+
}
|
|
441
|
+
async encryptConfigurationItemWith(src, settings, encrypt) {
|
|
408
442
|
if (this.usedPassphrase != "") {
|
|
409
|
-
return await
|
|
443
|
+
return await encrypt(src, this.usedPassphrase + SALT_OF_PASSPHRASE);
|
|
410
444
|
}
|
|
411
445
|
const passphrase = await this.getPassphrase(settings);
|
|
412
446
|
if (passphrase === false) {
|
|
@@ -416,7 +450,7 @@ class SettingService extends ServiceBase {
|
|
|
416
450
|
);
|
|
417
451
|
return "";
|
|
418
452
|
}
|
|
419
|
-
const dec = await
|
|
453
|
+
const dec = await encrypt(src, passphrase + SALT_OF_PASSPHRASE);
|
|
420
454
|
if (dec) {
|
|
421
455
|
this.usedPassphrase = passphrase;
|
|
422
456
|
return dec;
|
|
@@ -428,7 +462,27 @@ class SettingService extends ServiceBase {
|
|
|
428
462
|
* @param settings The settings to decrypt.
|
|
429
463
|
*/
|
|
430
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
|
+
}
|
|
431
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
|
+
}
|
|
432
486
|
if (passphrase === false) {
|
|
433
487
|
this._log("No passphrase found for data.json! Verify configuration before syncing.", LOG_LEVEL_URGENT);
|
|
434
488
|
const hasEncryptedRemoteConfigurations = Object.values(settings.remoteConfigurations || {}).some(
|
package/dist/settings.d.ts
CHANGED
|
@@ -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
|
|
14
|
+
export type { ObsidianLiveSyncSettings, RemoteDBSettings, RemoteTypeSettings } from "./common/models/setting.type.js";
|
package/dist/settings.js
CHANGED
|
@@ -12,6 +12,14 @@ import {
|
|
|
12
12
|
PREFERRED_SETTING_SELF_HOSTED
|
|
13
13
|
} from "./common/models/setting.const.preferred.js";
|
|
14
14
|
import { CURRENT_SETTING_VERSION } from "./common/models/setting.const.js";
|
|
15
|
+
import {
|
|
16
|
+
deriveIdKey,
|
|
17
|
+
deriveOrImportIdKey,
|
|
18
|
+
formatIdRecoveryCode,
|
|
19
|
+
configuredIdKey,
|
|
20
|
+
ID_DERIVATION_VERSION,
|
|
21
|
+
ID_RECOVERY_CODE_PREFIX
|
|
22
|
+
} from "./common/idDerivation.js";
|
|
15
23
|
import {
|
|
16
24
|
prepareSettingsForLoad,
|
|
17
25
|
SettingsMigrationReviewCodes
|
|
@@ -20,6 +28,8 @@ import { assessTweakCompatibility } from "./common/models/tweak.compatibility.js
|
|
|
20
28
|
export {
|
|
21
29
|
CURRENT_SETTING_VERSION,
|
|
22
30
|
DEFAULT_SETTINGS,
|
|
31
|
+
ID_DERIVATION_VERSION,
|
|
32
|
+
ID_RECOVERY_CODE_PREFIX,
|
|
23
33
|
NEW_VAULT_SETTINGS,
|
|
24
34
|
P2P_DEFAULT_SETTINGS,
|
|
25
35
|
PREFERRED_BASE,
|
|
@@ -29,6 +39,10 @@ export {
|
|
|
29
39
|
SETTINGS_SCHEMA_DEFAULTS,
|
|
30
40
|
SettingsMigrationReviewCodes,
|
|
31
41
|
assessTweakCompatibility,
|
|
42
|
+
configuredIdKey,
|
|
32
43
|
createNewVaultSettings,
|
|
44
|
+
deriveIdKey,
|
|
45
|
+
deriveOrImportIdKey,
|
|
46
|
+
formatIdRecoveryCode,
|
|
33
47
|
prepareSettingsForLoad
|
|
34
48
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup URI sharing for maintained hosts.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
export { encodeSettingsToSetupURI, encodeTimeBoundSetupURI, getTimeBoundSetupURIUsableUntil, isTimeBoundSetupURIUsableNow, decodeSettingsFromSetupURI, type TimeBoundSetupURIOptions, type TimeBoundSetupURIResult, type TimeBoundSetupURIMode, } from "./API/processSetting.js";
|
package/dist/setupUri.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
encodeSettingsToSetupURI,
|
|
3
|
+
encodeTimeBoundSetupURI,
|
|
4
|
+
getTimeBoundSetupURIUsableUntil,
|
|
5
|
+
isTimeBoundSetupURIUsableNow,
|
|
6
|
+
decodeSettingsFromSetupURI
|
|
7
|
+
} from "./API/processSetting.js";
|
|
8
|
+
export {
|
|
9
|
+
decodeSettingsFromSetupURI,
|
|
10
|
+
encodeSettingsToSetupURI,
|
|
11
|
+
encodeTimeBoundSetupURI,
|
|
12
|
+
getTimeBoundSetupURIUsableUntil,
|
|
13
|
+
isTimeBoundSetupURIUsableNow
|
|
14
|
+
};
|
|
@@ -8,7 +8,7 @@ export declare function stripAllPrefixes(prefixedPath: FilePathWithPrefix): File
|
|
|
8
8
|
export declare function addPrefix(path: FilePath | FilePathWithPrefix, prefix: string): FilePathWithPrefix;
|
|
9
9
|
export declare function expandFilePathPrefix(path: FilePathWithPrefix | FilePath): [string, FilePathWithPrefix];
|
|
10
10
|
export declare function expandDocumentIDPrefix(id: DocumentID): [string, FilePathWithPrefix];
|
|
11
|
-
export declare function path2id_base(filenameSrc: FilePathWithPrefix | FilePath, obfuscatePassphrase: string | false, caseInsensitive: boolean): Promise<DocumentID>;
|
|
11
|
+
export declare function path2id_base(filenameSrc: FilePathWithPrefix | FilePath, obfuscatePassphrase: string | false, caseInsensitive: boolean, idDerivationKey?: string): Promise<DocumentID>;
|
|
12
12
|
export declare function id2path_base(id: DocumentID, entry?: EntryHasPath): FilePathWithPrefix;
|
|
13
13
|
export declare function getPath(entry: AnyEntry): FilePathWithPrefix;
|
|
14
14
|
export declare function getPathWithoutPrefix(entry: AnyEntry): FilePath;
|
|
@@ -14,6 +14,7 @@ import { memorizeFuncWithLRUCache } from "../common/utils.js";
|
|
|
14
14
|
import { uint8ArrayToHexString, writeString } from "./convert.js";
|
|
15
15
|
import { unique } from "octagonal-wheels/collection.js";
|
|
16
16
|
import { CHeader, ICHeader, ICXHeader, PSCHeader } from "../common/models/fileaccess.const.js";
|
|
17
|
+
import { computeKeyedId } from "../common/idDerivation.js";
|
|
17
18
|
function isValidFilenameInWidows(filename) {
|
|
18
19
|
const regex = /[\u0000-\u001f]|[\\":?<>|*#]/g;
|
|
19
20
|
if (regex.test(filename)) return false;
|
|
@@ -68,7 +69,7 @@ const _hashString = memorizeFuncWithLRUCache(async (key) => {
|
|
|
68
69
|
function hashString(key) {
|
|
69
70
|
return _hashString(key);
|
|
70
71
|
}
|
|
71
|
-
async function path2id_base(filenameSrc, obfuscatePassphrase, caseInsensitive) {
|
|
72
|
+
async function path2id_base(filenameSrc, obfuscatePassphrase, caseInsensitive, idDerivationKey) {
|
|
72
73
|
if (filenameSrc.startsWith(PREFIX_OBFUSCATED)) return `${filenameSrc}`;
|
|
73
74
|
let filename = `${filenameSrc}`;
|
|
74
75
|
const newPrefix = obfuscatePassphrase ? PREFIX_OBFUSCATED : "";
|
|
@@ -82,8 +83,7 @@ async function path2id_base(filenameSrc, obfuscatePassphrase, caseInsensitive) {
|
|
|
82
83
|
}
|
|
83
84
|
const [prefix, body] = expandFilePathPrefix(x);
|
|
84
85
|
if (body.startsWith(PREFIX_OBFUSCATED)) return newPrefix + x;
|
|
85
|
-
const
|
|
86
|
-
const out = await hashString(`${hashedPassphrase}:${filename}`);
|
|
86
|
+
const out = idDerivationKey ? await computeKeyedId(idDerivationKey, "document", filename) : await hashString(`${await hashString(obfuscatePassphrase)}:${filename}`);
|
|
87
87
|
return prefix + newPrefix + out;
|
|
88
88
|
}
|
|
89
89
|
function id2path_base(id, entry) {
|