Compare commits

..
Author SHA1 Message Date
vorotamoroz ae264c3383 Lock patched devalue for 1.0.30 release 2026-09-18 09:11:20 +00:00
vorotamoroz df023f016e Clarify 1.0.30 synchronisation notes 2026-09-18 08:41:54 +00:00
github-actions[bot] 8dcd53090a Releasing 1.0.30 2026-09-18 08:40:49 +00:00
vorotamoroz 9998bd6dde Merge pull request #1204 from vrtmrz/fix/fast-fetch-security-seed-reads
Avoid repeated remote parameter reads during Fast Fetch and Journal transfers
2026-09-18 17:21:59 +09:00
vorotamoroz fa97f96960 Group pending synchronisation fixes in Unreleased notes 2026-09-18 08:14:38 +00:00
vorotamoroz 916ec4f276 Document restart conflict protection in 1.0.29 notes 2026-09-18 07:03:34 +00:00
vorotamoroz 8ea656a7da Use Commonlib 0.1.27 for Journal transfer readiness 2026-09-18 07:01:07 +00:00
vorotamoroz a3231b0906 Reuse Security Seed reads within each resource 2026-09-18 03:34:31 +00:00
vorotamoroz a8fe11e0e0 Merge pull request #1203 from vrtmrz/test/cli-mirror-provenance
Align CLI mirror tests with file provenance
2026-09-18 11:02:11 +09:00
12 changed files with 93 additions and 21 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "obsidian-livesync",
"name": "Self-hosted LiveSync",
"version": "1.0.29",
"version": "1.0.30",
"minAppVersion": "1.7.2",
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
"author": "vorotamoroz",
+12 -12
View File
@@ -1,12 +1,12 @@
{
"name": "obsidian-livesync",
"version": "1.0.29",
"version": "1.0.30",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "obsidian-livesync",
"version": "1.0.29",
"version": "1.0.30",
"license": "MIT",
"workspaces": [
"src/apps/cli",
@@ -23,7 +23,7 @@
"@smithy/types": "^4.14.3",
"@smithy/util-retry": "^4.4.5",
"@vrtmrz/browser-ui-kit": "0.1.0",
"@vrtmrz/livesync-commonlib": "0.1.26",
"@vrtmrz/livesync-commonlib": "0.1.27",
"@vrtmrz/obsidian-plugin-kit": "0.1.4",
"@vrtmrz/ui-interactions": "0.1.2",
"diff-match-patch": "^1.0.5",
@@ -4567,9 +4567,9 @@
}
},
"node_modules/@vrtmrz/livesync-commonlib": {
"version": "0.1.26",
"resolved": "https://registry.npmjs.org/@vrtmrz/livesync-commonlib/-/livesync-commonlib-0.1.26.tgz",
"integrity": "sha512-AVJky976PP1M+g18im6tJ1eKSq82uN73vkS98WWZWvMJ1UDz6O8YRVWa9dkFakAVXecdxxxLkiD45g5HTRnn6Q==",
"version": "0.1.27",
"resolved": "https://registry.npmjs.org/@vrtmrz/livesync-commonlib/-/livesync-commonlib-0.1.27.tgz",
"integrity": "sha512-n/AIqXPGN9Z7zmq0QSKnlE5uY5hTZgXW6VM6NJERR0/ggx3TTv1b3WsSbiLpCeU7Mu6KbPqC2BSjr4jCGvSdCA==",
"license": "MIT",
"dependencies": {
"@aws-sdk/client-s3": "^3.808.0",
@@ -5629,9 +5629,9 @@
}
},
"node_modules/devalue": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz",
"integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==",
"version": "5.9.2",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.2.tgz",
"integrity": "sha512-po4PAY5c53tw5XMocSnf8A/5OHhbbUftpr93aEN6BBoAdntUmK7vu7wOATqvt7cXO7m1Cl4gMVn6p7n6n4mj0w==",
"devOptional": true,
"license": "MIT"
},
@@ -12665,7 +12665,7 @@
},
"src/apps/cli": {
"name": "self-hosted-livesync-cli",
"version": "1.0.29-cli",
"version": "1.0.30-cli",
"dependencies": {
"chokidar": "^4.0.0",
"minimatch": "^10.2.5",
@@ -12690,7 +12690,7 @@
},
"src/apps/webapp": {
"name": "livesync-webapp",
"version": "1.0.29-webapp",
"version": "1.0.30-webapp",
"dependencies": {
"octagonal-wheels": "^0.1.54"
},
@@ -12702,7 +12702,7 @@
}
},
"src/apps/webpeer": {
"version": "1.0.29-webpeer",
"version": "1.0.30-webpeer",
"dependencies": {
"octagonal-wheels": "^0.1.54"
},
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "obsidian-livesync",
"version": "1.0.29",
"version": "1.0.30",
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
"main": "main.js",
"type": "module",
@@ -183,7 +183,7 @@
"@smithy/types": "^4.14.3",
"@smithy/util-retry": "^4.4.5",
"@vrtmrz/browser-ui-kit": "0.1.0",
"@vrtmrz/livesync-commonlib": "0.1.26",
"@vrtmrz/livesync-commonlib": "0.1.27",
"@vrtmrz/obsidian-plugin-kit": "0.1.4",
"@vrtmrz/ui-interactions": "0.1.2",
"diff-match-patch": "^1.0.5",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "self-hosted-livesync-cli",
"private": true,
"version": "1.0.29-cli",
"version": "1.0.30-cli",
"main": "dist/index.cjs",
"type": "module",
"scripts": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "livesync-webapp",
"private": true,
"version": "1.0.29-webapp",
"version": "1.0.30-webapp",
"type": "module",
"description": "Browser-based Self-hosted LiveSync using FileSystem API",
"scripts": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "webpeer",
"private": true,
"version": "1.0.29-webpeer",
"version": "1.0.30-webpeer",
"type": "module",
"scripts": {
"dev": "vite",
+2 -1
View File
@@ -3,6 +3,7 @@ import {
CAPABILITY_NOT_APPLICABLE,
CENTRAL_REMOTE_REPLICATION_READINESS,
NO_INTERACTION,
PROVIDER_OWNED_CENTRAL_REMOTE_REPLICATION_READINESS,
REPLICATION_PROGRESS_PRESENTATIONS,
REMOTE_RESOURCE_KINDS,
defineReplicatorProviderDefinitions,
@@ -134,7 +135,7 @@ export function createCentralReplicatorProviderDefinitions(
[REMOTE_MINIO]: {
kind: REMOTE_MINIO,
diagnosticName: "Object Storage",
readiness: CENTRAL_REMOTE_REPLICATION_READINESS,
readiness: PROVIDER_OWNED_CENTRAL_REMOTE_REPLICATION_READINESS,
isConfigured: (settings) =>
settings.remoteType === REMOTE_MINIO && !!settings.endpoint?.trim() && !!settings.bucket?.trim(),
configurationIdentity: getObjectStorageReplicatorConfigurationIdentity,
@@ -47,6 +47,12 @@ describe("central Replicator provider definitions", () => {
.toEqual(["connection", "preferred-tweak", "security-seed", "synchronisation-information"].sort());
});
it("lets Journal prepare its own fresh Security Seed while CouchDB uses central preparation", () => {
const definitions = createCentralReplicatorProviderDefinitions({} as never);
expect(definitions.get(REMOTE_COUCHDB)?.readiness.centralRemotePreparation).toBe("required");
expect(definitions.get(REMOTE_MINIO)?.readiness.centralRemotePreparation).toBe("provider-owned");
});
it("composes CouchDB and Object Storage policies outside LiveSyncBaseCore", async () => {
const host = {} as Parameters<typeof createCentralReplicatorProviderDefinitions>[0];
const definitions = createCentralReplicatorProviderDefinitions(host);
@@ -285,6 +285,46 @@ describe("replicator probe factories", () => {
expect(objectReplicator.closeReplication).toHaveBeenCalledOnce();
});
it("reads the Security Seed once per resource, including concurrent reads, and refreshes for a new resource", async () => {
const settings = createSettings();
const factory = createCouchDBSecuritySeedResourceFactory({} as never);
const firstResource = await factory(settings);
const firstReplicator = mocks.couchDB[0];
const firstSeed = new Uint8Array([1]);
firstReplicator.getReplicationPBKDF2Salt.mockResolvedValue(firstSeed);
const [first, concurrent] = await Promise.all([firstResource.read(), firstResource.read()]);
expect(first).toBe(firstSeed);
expect(concurrent).toBe(firstSeed);
await expect(firstResource.read()).resolves.toBe(firstSeed);
expect(firstReplicator.getReplicationPBKDF2Salt).toHaveBeenCalledOnce();
expect(firstReplicator.getReplicationPBKDF2Salt).toHaveBeenCalledWith({ ...settings }, true);
await firstResource.dispose();
const nextResource = await factory(settings);
const nextReplicator = mocks.couchDB[1];
const nextSeed = new Uint8Array([2]);
nextReplicator.getReplicationPBKDF2Salt.mockResolvedValue(nextSeed);
await expect(nextResource.read()).resolves.toBe(nextSeed);
expect(nextReplicator.getReplicationPBKDF2Salt).toHaveBeenCalledOnce();
expect(nextReplicator.getReplicationPBKDF2Salt).toHaveBeenCalledWith({ ...settings }, true);
await nextResource.dispose();
});
it("retries a failed Security Seed read within the same resource", async () => {
const resource = await createCouchDBSecuritySeedResourceFactory({} as never)(createSettings());
const replicator = mocks.couchDB[0];
const failure = new Error("connection interrupted");
const seed = new Uint8Array([1]);
replicator.getReplicationPBKDF2Salt.mockRejectedValueOnce(failure).mockResolvedValueOnce(seed);
await expect(resource.read()).rejects.toBe(failure);
await expect(resource.read()).resolves.toBe(seed);
await expect(resource.read()).resolves.toBe(seed);
expect(replicator.getReplicationPBKDF2Salt).toHaveBeenCalledTimes(2);
await resource.dispose();
});
it("checks synchronisation information through an owned connection and disposes the private Replicator", async () => {
const settings = createSettings();
const snapshot = { ...settings };
+12 -1
View File
@@ -21,8 +21,19 @@ function createSecuritySeedResourceFactory(
return (setting) => {
const snapshot = snapshotRemoteSettings(setting);
const replicator = createReplicator();
let readPromise: Promise<Uint8Array<ArrayBuffer>> | undefined;
const read = () => {
if (readPromise) return readPromise;
const pending = Promise.resolve().then(() => replicator.getReplicationPBKDF2Salt(snapshot, true));
readPromise = pending;
// A failed read must not poison a later retry within the same resource.
void pending.catch(() => {
if (readPromise === pending) readPromise = undefined;
});
return pending;
};
return Promise.resolve({
read: () => replicator.getReplicationPBKDF2Salt(snapshot, true),
read,
dispose: createReplicatorDisposer(replicator),
});
};
+13
View File
@@ -12,6 +12,19 @@ Earlier releases remain available in the 1.0 release history, the 1.0 preview hi
## Unreleased
## 1.0.30
18th September, 2026
### Synchronisation
#### Fixed
- After a restart, unchanged local files no longer overwrite newer synchronised content. (#994)
- When LiveSync cannot establish a local file's origin, it keeps the file as a conflict for you to review. This also applies to ordinary file synchronisation in the command-line tool.
- Fast Fetch completes initial setup with fewer remote requests.
- Object Storage synchronisation makes fewer remote requests while still checking its parameters before writing.
## 1.0.29
16th September, 2026
+2 -1
View File
@@ -40,5 +40,6 @@
"1.0.26": "1.7.2",
"1.0.27": "1.7.2",
"1.0.28": "1.7.2",
"1.0.29": "1.7.2"
"1.0.29": "1.7.2",
"1.0.30": "1.7.2"
}