### Fixed

- The encryption algorithm now uses HKDF with a master key.
- `Fetch everything from the remote` now works correctly.
- Extra log messages during QR code decoding have been removed.

### Changed
- Some settings have been moved to the `Patches` pane:

### Behavioural and API Changes
- `DirectFileManipulatorV2` now requires new settings (as you may already know, E2EEAlgorithm).
- The database version has been increased to `12` from `10`.
This commit is contained in:
vorotamoroz
2025-07-14 00:33:40 +09:00
parent ce4b61557a
commit e0e7e1b5ca
14 changed files with 80 additions and 54 deletions

14
package-lock.json generated
View File

@@ -20,7 +20,7 @@
"fflate": "^0.8.2", "fflate": "^0.8.2",
"idb": "^8.0.3", "idb": "^8.0.3",
"minimatch": "^10.0.1", "minimatch": "^10.0.1",
"octagonal-wheels": "^0.1.31", "octagonal-wheels": "^0.1.35",
"qrcode-generator": "^1.4.4", "qrcode-generator": "^1.4.4",
"svelte-check": "^4.1.7", "svelte-check": "^4.1.7",
"trystero": "^0.21.5", "trystero": "^0.21.5",
@@ -8704,9 +8704,9 @@
} }
}, },
"node_modules/octagonal-wheels": { "node_modules/octagonal-wheels": {
"version": "0.1.31", "version": "0.1.35",
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.31.tgz", "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.35.tgz",
"integrity": "sha512-aR/QWyon6KUeW4UlJPRGKVqIiJLz4otm4F6PWOQp8aZAdqYIe/VYT5FBlaqQzCRNDrvkjVOvrHOlBGKIU++thw==", "integrity": "sha512-fjyvgg1+aG4SnpPjdZp6SPA/N6CseTgTLWnYWFN0mdH6qVAZzxNkDxKACtPxuxRQfgjj83yiPhQBMT3yA0XUnw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"idb": "^8.0.3" "idb": "^8.0.3"
@@ -17515,9 +17515,9 @@
} }
}, },
"octagonal-wheels": { "octagonal-wheels": {
"version": "0.1.31", "version": "0.1.35",
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.31.tgz", "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.35.tgz",
"integrity": "sha512-aR/QWyon6KUeW4UlJPRGKVqIiJLz4otm4F6PWOQp8aZAdqYIe/VYT5FBlaqQzCRNDrvkjVOvrHOlBGKIU++thw==", "integrity": "sha512-fjyvgg1+aG4SnpPjdZp6SPA/N6CseTgTLWnYWFN0mdH6qVAZzxNkDxKACtPxuxRQfgjj83yiPhQBMT3yA0XUnw==",
"requires": { "requires": {
"idb": "^8.0.3" "idb": "^8.0.3"
} }

View File

@@ -89,7 +89,7 @@
"fflate": "^0.8.2", "fflate": "^0.8.2",
"idb": "^8.0.3", "idb": "^8.0.3",
"minimatch": "^10.0.1", "minimatch": "^10.0.1",
"octagonal-wheels": "^0.1.31", "octagonal-wheels": "^0.1.35",
"qrcode-generator": "^1.4.4", "qrcode-generator": "^1.4.4",
"svelte-check": "^4.1.7", "svelte-check": "^4.1.7",
"trystero": "^0.21.5", "trystero": "^0.21.5",

Submodule src/lib updated: dfbd6358b1...ab02f72aa5

View File

@@ -292,7 +292,8 @@ export default class ObsidianLiveSyncPlugin
skipInfo: boolean, skipInfo: boolean,
compression: boolean, compression: boolean,
customHeaders: Record<string, string>, customHeaders: Record<string, string>,
useRequestAPI: boolean useRequestAPI: boolean,
getPBKDF2Salt: () => Promise<Uint8Array>
): Promise< ): Promise<
| string | string
| { | {

View File

@@ -11,7 +11,7 @@ import { AbstractModule } from "../AbstractModule.ts";
import type { Rebuilder } from "../interfaces/DatabaseRebuilder.ts"; import type { Rebuilder } from "../interfaces/DatabaseRebuilder.ts";
import type { ICoreModule } from "../ModuleTypes.ts"; import type { ICoreModule } from "../ModuleTypes.ts";
import type { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator.ts"; import type { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator.ts";
import { fetchAllUsedChunks } from "../../lib/src/pouchdb/utils_couchdb.ts"; import { fetchAllUsedChunks } from "@/lib/src/pouchdb/chunks.ts";
import { EVENT_DATABASE_REBUILT, eventHub } from "src/common/events.ts"; import { EVENT_DATABASE_REBUILT, eventHub } from "src/common/events.ts";
export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebuilder { export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebuilder {
@@ -90,8 +90,8 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
return this.rebuildEverything(); return this.rebuildEverything();
} }
$fetchLocal(makeLocalChunkBeforeSync?: boolean): Promise<void> { $fetchLocal(makeLocalChunkBeforeSync?: boolean, preventMakeLocalFilesBeforeSync?: boolean): Promise<void> {
return this.fetchLocal(makeLocalChunkBeforeSync); return this.fetchLocal(makeLocalChunkBeforeSync, preventMakeLocalFilesBeforeSync);
} }
async scheduleRebuild(): Promise<void> { async scheduleRebuild(): Promise<void> {

View File

@@ -4,7 +4,8 @@ import { AbstractModule } from "../AbstractModule";
import type { ICoreModule } from "../ModuleTypes"; import type { ICoreModule } from "../ModuleTypes";
import { Logger, LOG_LEVEL_NOTICE, LOG_LEVEL_INFO, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; import { Logger, LOG_LEVEL_NOTICE, LOG_LEVEL_INFO, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
import { isLockAcquired, shareRunningResult, skipIfDuplicated } from "octagonal-wheels/concurrency/lock"; import { isLockAcquired, shareRunningResult, skipIfDuplicated } from "octagonal-wheels/concurrency/lock";
import { purgeUnreferencedChunks, balanceChunkPurgedDBs } from "../../lib/src/pouchdb/utils_couchdb"; import { balanceChunkPurgedDBs } from "@/lib/src/pouchdb/chunks";
import { purgeUnreferencedChunks } from "@/lib/src/pouchdb/chunks";
import { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator"; import { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator";
import { throttle } from "octagonal-wheels/function"; import { throttle } from "octagonal-wheels/function";
import { arrayToChunkedArray } from "octagonal-wheels/collection"; import { arrayToChunkedArray } from "octagonal-wheels/collection";
@@ -79,8 +80,18 @@ export class ModuleReplicator extends AbstractModule implements ICoreModule {
$everyOnResetDatabase(db: LiveSyncLocalDB): Promise<boolean> { $everyOnResetDatabase(db: LiveSyncLocalDB): Promise<boolean> {
return this.setReplicator(); return this.setReplicator();
} }
async ensureReplicatorPBKDF2Salt(showMessage: boolean = false): Promise<boolean> {
// Checking salt
const replicator = this.core.$$getReplicator();
return await replicator.ensurePBKDF2Salt(this.settings, showMessage, true);
}
async $everyBeforeReplicate(showMessage: boolean): Promise<boolean> { async $everyBeforeReplicate(showMessage: boolean): Promise<boolean> {
// Checking salt
if (!(await this.ensureReplicatorPBKDF2Salt(showMessage))) {
Logger("Failed to ensure PBKDF2 salt for replication.", LOG_LEVEL_NOTICE);
return false;
}
await this.loadQueuedFiles(); await this.loadQueuedFiles();
return true; return true;
} }

View File

@@ -121,9 +121,9 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
} }
); );
let makeLocalChunkBeforeSync = false; let makeLocalChunkBeforeSync = false;
let preventMakeLocalFilesBeforeSync = false; let makeLocalFilesBeforeSync = false;
if (chunkMode === method1) { if (chunkMode === method1) {
preventMakeLocalFilesBeforeSync = true; makeLocalFilesBeforeSync = true;
} else if (chunkMode === method2) { } else if (chunkMode === method2) {
makeLocalChunkBeforeSync = true; makeLocalChunkBeforeSync = true;
} else if (chunkMode === method3) { } else if (chunkMode === method3) {
@@ -133,7 +133,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
return false; return false;
} }
await this.core.rebuilder.$fetchLocal(makeLocalChunkBeforeSync, preventMakeLocalFilesBeforeSync); await this.core.rebuilder.$fetchLocal(makeLocalChunkBeforeSync, !makeLocalFilesBeforeSync);
await this.deleteRedFlag3(); await this.deleteRedFlag3();
if (this.settings.suspendFileWatching) { if (this.settings.suspendFileWatching) {

View File

@@ -84,8 +84,8 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
$msg("Doctor.Dialogue.MainFix", { $msg("Doctor.Dialogue.MainFix", {
name: getConfName(key as AllSettingItemKey), name: getConfName(key as AllSettingItemKey),
current: `${this.settings[key]}`, current: `${this.settings[key]}`,
reason: value.reason ?? " N/A ", reason: value.reasonFunc?.(this.settings) ?? value.reason ?? " N/A ",
ideal: `${value.valueDisplay ?? value.value}`, ideal: `${value.valueDisplayFunc ? value.valueDisplayFunc(this.settings) : value.value}`,
//@ts-ignore //@ts-ignore
level: `${level}`, level: `${level}`,
note: note, note: note,

View File

@@ -3,13 +3,10 @@ import { LOG_LEVEL_DEBUG, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-
import { Notice, requestUrl, type RequestUrlParam, type RequestUrlResponse } from "../../deps.ts"; import { Notice, requestUrl, type RequestUrlParam, type RequestUrlResponse } from "../../deps.ts";
import { type CouchDBCredentials, type EntryDoc, type FilePathWithPrefix } from "../../lib/src/common/types.ts"; import { type CouchDBCredentials, type EntryDoc, type FilePathWithPrefix } from "../../lib/src/common/types.ts";
import { getPathFromTFile } from "../../common/utils.ts"; import { getPathFromTFile } from "../../common/utils.ts";
import { import { isCloudantURI, isValidRemoteCouchDBURI } from "../../lib/src/pouchdb/utils_couchdb.ts";
disableEncryption, import { replicationFilter } from "@/lib/src/pouchdb/compress.ts";
enableEncryption, import { disableEncryption } from "@/lib/src/pouchdb/encryption.ts";
isCloudantURI, import { enableEncryption } from "@/lib/src/pouchdb/encryption.ts";
isValidRemoteCouchDBURI,
replicationFilter,
} from "../../lib/src/pouchdb/utils_couchdb.ts";
import { setNoticeClass } from "../../lib/src/mock_and_interop/wrapper.ts"; import { setNoticeClass } from "../../lib/src/mock_and_interop/wrapper.ts";
import { ObsHttpHandler } from "./APILib/ObsHttpHandler.ts"; import { ObsHttpHandler } from "./APILib/ObsHttpHandler.ts";
import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.ts"; import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.ts";
@@ -103,7 +100,8 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
skipInfo: boolean, skipInfo: boolean,
compression: boolean, compression: boolean,
customHeaders: Record<string, string>, customHeaders: Record<string, string>,
useRequestAPI: boolean useRequestAPI: boolean,
getPBKDF2Salt: () => Promise<Uint8Array>
): Promise<string | { db: PouchDB.Database<EntryDoc>; info: PouchDB.Core.DatabaseInfo }> { ): Promise<string | { db: PouchDB.Database<EntryDoc>; info: PouchDB.Core.DatabaseInfo }> {
if (!isValidRemoteCouchDBURI(uri)) return "Remote URI is not valid"; if (!isValidRemoteCouchDBURI(uri)) return "Remote URI is not valid";
if (uri.toLowerCase() != uri) return "Remote URI and database name could not contain capital letters."; if (uri.toLowerCase() != uri) return "Remote URI and database name could not contain capital letters.";
@@ -229,7 +227,14 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
replicationFilter(db, compression); replicationFilter(db, compression);
disableEncryption(); disableEncryption();
if (passphrase !== "false" && typeof passphrase === "string") { if (passphrase !== "false" && typeof passphrase === "string") {
enableEncryption(db, passphrase, useDynamicIterationCount, false); enableEncryption(
db,
passphrase,
useDynamicIterationCount,
false,
getPBKDF2Salt,
this.settings.E2EEAlgorithm
);
} }
if (skipInfo) { if (skipInfo) {
return { db: db, info: { db_name: "", doc_count: 0, update_seq: "" } }; return { db: db, info: { db_name: "", doc_count: 0, update_seq: "" } };

View File

@@ -101,7 +101,6 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
//@ts-ignore //@ts-ignore
newSettings[settingKey] = settingValue; newSettings[settingKey] = settingValue;
} }
console.warn(newSettings);
await this.applySettingWizard(this.settings, newSettings, "QR Code"); await this.applySettingWizard(this.settings, newSettings, "QR Code");
} }
async command_copySetupURI(stripExtra = true) { async command_copySetupURI(stripExtra = true) {

View File

@@ -16,11 +16,9 @@ import {
import { delay, isObjectDifferent, sizeToHumanReadable } from "../../../lib/src/common/utils.ts"; import { delay, isObjectDifferent, sizeToHumanReadable } from "../../../lib/src/common/utils.ts";
import { versionNumberString2Number } from "../../../lib/src/string_and_binary/convert.ts"; import { versionNumberString2Number } from "../../../lib/src/string_and_binary/convert.ts";
import { Logger } from "../../../lib/src/common/logger.ts"; import { Logger } from "../../../lib/src/common/logger.ts";
import { import { checkSyncInfo } from "@/lib/src/pouchdb/negotiation.ts";
balanceChunkPurgedDBs, import { balanceChunkPurgedDBs } from "@/lib/src/pouchdb/chunks.ts";
checkSyncInfo, import { purgeUnreferencedChunks } from "@/lib/src/pouchdb/chunks.ts";
purgeUnreferencedChunks,
} from "../../../lib/src/pouchdb/utils_couchdb.ts";
import { testCrypt } from "../../../lib/src/encryption/e2ee_v2.ts"; import { testCrypt } from "../../../lib/src/encryption/e2ee_v2.ts";
import ObsidianLiveSyncPlugin from "../../../main.ts"; import ObsidianLiveSyncPlugin from "../../../main.ts";
import { scheduleTask } from "../../../common/utils.ts"; import { scheduleTask } from "../../../common/utils.ts";

View File

@@ -1,4 +1,9 @@
import { type HashAlgorithm, LOG_LEVEL_NOTICE } from "../../../lib/src/common/types.ts"; import {
E2EEAlgorithmNames,
E2EEAlgorithms,
type HashAlgorithm,
LOG_LEVEL_NOTICE,
} from "../../../lib/src/common/types.ts";
import { Logger } from "../../../lib/src/common/logger.ts"; import { Logger } from "../../../lib/src/common/logger.ts";
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts"; import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
@@ -37,6 +42,19 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
void addPanel(paneEl, "Compatibility (Internal API Usage)").then((paneEl) => { void addPanel(paneEl, "Compatibility (Internal API Usage)").then((paneEl) => {
new Setting(paneEl).autoWireToggle("watchInternalFileChanges", { invert: true }); new Setting(paneEl).autoWireToggle("watchInternalFileChanges", { invert: true });
}); });
void addPanel(paneEl, "Compatibility (Remote Database)").then((paneEl) => {
new Setting(paneEl).autoWireDropDown("E2EEAlgorithm", {
options: E2EEAlgorithmNames,
});
});
new Setting(paneEl).autoWireToggle("useDynamicIterationCount", {
holdValue: true,
onUpdate: visibleOnly(
() =>
this.isConfiguredAs("E2EEAlgorithm", E2EEAlgorithms.ForceV1) ||
this.isConfiguredAs("E2EEAlgorithm", E2EEAlgorithms.V1)
),
});
void addPanel(paneEl, "Edge case addressing (Database)").then((paneEl) => { void addPanel(paneEl, "Edge case addressing (Database)").then((paneEl) => {
new Setting(paneEl) new Setting(paneEl)
@@ -79,4 +97,16 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
void addPanel(paneEl, "Compatibility (Trouble addressed)").then((paneEl) => { void addPanel(paneEl, "Compatibility (Trouble addressed)").then((paneEl) => {
new Setting(paneEl).autoWireToggle("disableCheckingConfigMismatch"); new Setting(paneEl).autoWireToggle("disableCheckingConfigMismatch");
}); });
void addPanel(paneEl, "Remote Database Tweak (In sunset)").then((paneEl) => {
new Setting(paneEl).autoWireToggle("useEden").setClass("wizardHidden");
const onlyUsingEden = visibleOnly(() => this.isConfiguredAs("useEden", true));
new Setting(paneEl).autoWireNumeric("maxChunksInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden");
new Setting(paneEl)
.autoWireNumeric("maxTotalLengthInEden", { onUpdate: onlyUsingEden })
.setClass("wizardHidden");
new Setting(paneEl).autoWireNumeric("maxAgeInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden");
new Setting(paneEl).autoWireToggle("enableCompression").setClass("wizardHidden");
});
} }

View File

@@ -2,24 +2,12 @@ import { type ConfigPassphraseStore } from "../../../lib/src/common/types.ts";
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts"; import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
import type { PageFunctions } from "./SettingPane.ts"; import type { PageFunctions } from "./SettingPane.ts";
import { visibleOnly } from "./SettingPane.ts";
export function panePowerUsers( export function panePowerUsers(
this: ObsidianLiveSyncSettingTab, this: ObsidianLiveSyncSettingTab,
paneEl: HTMLElement, paneEl: HTMLElement,
{ addPanel }: PageFunctions { addPanel }: PageFunctions
): void { ): void {
void addPanel(paneEl, "Remote Database Tweak").then((paneEl) => {
new Setting(paneEl).autoWireToggle("useEden").setClass("wizardHidden");
const onlyUsingEden = visibleOnly(() => this.isConfiguredAs("useEden", true));
new Setting(paneEl).autoWireNumeric("maxChunksInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden");
new Setting(paneEl)
.autoWireNumeric("maxTotalLengthInEden", { onUpdate: onlyUsingEden })
.setClass("wizardHidden");
new Setting(paneEl).autoWireNumeric("maxAgeInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden");
new Setting(paneEl).autoWireToggle("enableCompression").setClass("wizardHidden");
});
void addPanel(paneEl, "CouchDB Connection Tweak", undefined, this.onlyOnCouchDB).then((paneEl) => { void addPanel(paneEl, "CouchDB Connection Tweak", undefined, this.onlyOnCouchDB).then((paneEl) => {
paneEl.addClass("wizardHidden"); paneEl.addClass("wizardHidden");

View File

@@ -579,12 +579,6 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
holdValue: true, holdValue: true,
onUpdate: isEncryptEnabled, onUpdate: isEncryptEnabled,
}); });
new Setting(paneEl)
.autoWireToggle("useDynamicIterationCount", {
holdValue: true,
onUpdate: isEncryptEnabled,
})
.setClass("wizardHidden");
}); });
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleFetchSettings")).then((paneEl) => { void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleFetchSettings")).then((paneEl) => {