mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-10-04 16:32:31 +00:00
Prepare time-bound Setup URI integration for Commonlib release
Add Time-bound and Compatible generation to the Obsidian dialogue, browser peer check, and setup tools, with boundary and compatibility coverage. Show the fixed window end before sharing a Time-bound URI. This review branch depends on unpublished @vrtmrz/livesync-commonlib 0.1.32-next.0. The root npm pin and Deno lockfile remain unchanged until that candidate is available; a fresh install of this commit is not yet expected to build.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// Keep CouchDB database-version negotiation isolated from Setup URI generation.
|
||||
// The exact release must match utils/livesync-commonlib-version.ts; the setup
|
||||
// tool suite checks every static specifier before release.
|
||||
export { checkRemoteVersion } from "npm:@vrtmrz/livesync-commonlib@0.1.0-rc.4/compat/pouchdb/negotiation";
|
||||
export { PouchDB } from "npm:@vrtmrz/livesync-commonlib@0.1.0-rc.4/compat/pouchdb/pouchdb-browser";
|
||||
export { checkRemoteVersion } from "npm:@vrtmrz/livesync-commonlib@0.1.32-next.0/compat/pouchdb/negotiation";
|
||||
export { PouchDB } from "npm:@vrtmrz/livesync-commonlib@0.1.32-next.0/compat/pouchdb/pouchdb-browser";
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
// Commonlib registry release. Static npm specifiers cannot interpolate this
|
||||
// value, so livesync-commonlib-version.test.ts verifies the domain-specific
|
||||
// facades against it.
|
||||
export const LIVESYNC_COMMONLIB_VERSION = "0.1.0-rc.4";
|
||||
export const LIVESYNC_COMMONLIB_VERSION = "0.1.32-next.0";
|
||||
|
||||
@@ -45,6 +45,8 @@ deno run --minimum-dependency-age=0 --config=./flyio/deno.jsonc --frozen --lock=
|
||||
|
||||
If `uri_passphrase` is omitted, the tool generates and prints a cryptographically random one. Store the Setup URI and its passphrase separately. The `passphrase` value protects synchronised Vault data and must also be stored safely.
|
||||
|
||||
The generator defaults to `uri_mode=ephemeral`. The URI opens only during the current fixed seven-day UTC window, and the tool prints its exact end time. It may have less than seven days remaining when generated. For an indefinitely reusable URI, set `uri_mode=persistent` before running the command. Persistent uses the existing encrypted format, which older clients that already support that format can read. The time condition controls opening the URI; it does not revoke settings or credentials after import.
|
||||
|
||||
### Object Storage
|
||||
|
||||
```sh
|
||||
|
||||
@@ -20,6 +20,14 @@ Deno.test("generates an Object Storage Setup URI with a selected S3 profile", as
|
||||
passphrase: "vault-secret",
|
||||
uri_passphrase: "setup-secret",
|
||||
});
|
||||
assert(
|
||||
generated.mode === "ephemeral",
|
||||
"the default Setup URI mode was not Ephemeral",
|
||||
);
|
||||
assert(
|
||||
generated.usableUntil !== null && generated.usableUntil > Date.now(),
|
||||
"the Ephemeral Setup URI did not report its usable end time",
|
||||
);
|
||||
const decoded = await decodeSettingsFromSetupURI(
|
||||
generated.setupURI,
|
||||
generated.setupPassphrase,
|
||||
@@ -121,3 +129,33 @@ Deno.test("generates a random-room P2P Setup URI without copying a device identi
|
||||
"the selected profile was not a P2P connection URI",
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("generates a Persistent Setup URI on explicit request", async () => {
|
||||
const generated = await generateSetupURI({
|
||||
remote_type: "p2p",
|
||||
passphrase: "vault-secret",
|
||||
uri_passphrase: "setup-secret",
|
||||
uri_mode: "persistent",
|
||||
});
|
||||
assert(generated.mode === "persistent", "the explicit mode was not retained");
|
||||
assert(
|
||||
generated.usableUntil === null,
|
||||
"Persistent unexpectedly has a time condition",
|
||||
);
|
||||
const decoded = await decodeSettingsFromSetupURI(
|
||||
generated.setupURI,
|
||||
generated.setupPassphrase,
|
||||
);
|
||||
assert(decoded, "the Persistent Setup URI could not be opened");
|
||||
});
|
||||
|
||||
Deno.test("rejects an unknown Setup URI mode", async () => {
|
||||
let rejected = false;
|
||||
try {
|
||||
await generateSetupURI({ uri_mode: "later" });
|
||||
} catch (error) {
|
||||
rejected = error instanceof Error &&
|
||||
error.message === "uri_mode must be ephemeral or persistent";
|
||||
}
|
||||
assert(rejected, "the generator accepted an unknown Setup URI mode");
|
||||
});
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import {
|
||||
createNewVaultSettings,
|
||||
encodeSettingsToSetupURI,
|
||||
encodeTimeBoundSetupURI,
|
||||
generateP2PRoomId,
|
||||
isTimeBoundSetupURIUsableNow,
|
||||
type ObsidianLiveSyncSettings,
|
||||
P2P_DEFAULT_SETTINGS,
|
||||
PREFERRED_BASE,
|
||||
PREFERRED_JOURNAL_SYNC,
|
||||
PREFERRED_SETTING_SELF_HOSTED,
|
||||
type TimeBoundSetupURIMode,
|
||||
upsertRemoteConfigurationInPlace,
|
||||
} from "./livesync-commonlib.ts";
|
||||
|
||||
@@ -19,6 +21,8 @@ export interface GeneratedSetupURI {
|
||||
remoteType: SetupRemoteType;
|
||||
setupURI: string;
|
||||
setupPassphrase: string;
|
||||
mode: TimeBoundSetupURIMode;
|
||||
usableUntil: number | null;
|
||||
}
|
||||
|
||||
function requireValue(
|
||||
@@ -147,6 +151,14 @@ function parseRemoteType(
|
||||
throw new Error("remote_type must be couchdb, s3, or p2p");
|
||||
}
|
||||
|
||||
function parseSetupURIMode(
|
||||
environment: SetupGeneratorEnvironment,
|
||||
): TimeBoundSetupURIMode {
|
||||
const mode = environment.uri_mode?.trim().toLowerCase() || "ephemeral";
|
||||
if (mode === "ephemeral" || mode === "persistent") return mode;
|
||||
throw new Error("uri_mode must be ephemeral or persistent");
|
||||
}
|
||||
|
||||
export function createSetupSettings(
|
||||
environment: SetupGeneratorEnvironment,
|
||||
): { remoteType: SetupRemoteType; settings: ObsidianLiveSyncSettings } {
|
||||
@@ -165,19 +177,54 @@ export async function generateSetupURI(
|
||||
): Promise<GeneratedSetupURI> {
|
||||
const setupPassphrase = environment.uri_passphrase?.trim() ||
|
||||
generateSecret();
|
||||
const mode = parseSetupURIMode(environment);
|
||||
const { remoteType, settings } = createSetupSettings(environment);
|
||||
const setupURI = await encodeSettingsToSetupURI(settings, setupPassphrase, [
|
||||
"pluginSyncExtendedSetting",
|
||||
"doNotUseFixedRevisionForChunks",
|
||||
], true);
|
||||
return { remoteType, setupURI: setupURI.trim(), setupPassphrase };
|
||||
const { uri, usableUntil } = await encodeTimeBoundSetupURI(
|
||||
settings,
|
||||
setupPassphrase,
|
||||
{
|
||||
mode,
|
||||
removeProperties: [
|
||||
"pluginSyncExtendedSetting",
|
||||
"doNotUseFixedRevisionForChunks",
|
||||
],
|
||||
skipDefaultValue: true,
|
||||
},
|
||||
);
|
||||
if (!isTimeBoundSetupURIUsableNow(usableUntil)) {
|
||||
throw new Error("Setup URI time window changed during generation");
|
||||
}
|
||||
return {
|
||||
remoteType,
|
||||
setupURI: uri.trim(),
|
||||
setupPassphrase,
|
||||
mode,
|
||||
usableUntil,
|
||||
};
|
||||
}
|
||||
|
||||
export async function runSetupURIGenerator(
|
||||
environment: SetupGeneratorEnvironment = Deno.env.toObject(),
|
||||
): Promise<void> {
|
||||
const generated = await generateSetupURI(environment);
|
||||
let generated = await generateSetupURI(environment);
|
||||
if (!isTimeBoundSetupURIUsableNow(generated.usableUntil)) {
|
||||
generated = await generateSetupURI(environment);
|
||||
}
|
||||
if (!isTimeBoundSetupURIUsableNow(generated.usableUntil)) {
|
||||
throw new Error("Setup URI time window changed before it could be shown");
|
||||
}
|
||||
console.log(`\nGenerated ${generated.remoteType} Setup URI.`);
|
||||
if (generated.usableUntil === null) {
|
||||
console.log(
|
||||
"Persistent: no time condition. Older clients can open this format.",
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
`Ephemeral: usable until ${
|
||||
new Date(generated.usableUntil).toISOString()
|
||||
} (UTC).`,
|
||||
);
|
||||
}
|
||||
console.log(
|
||||
"Your passphrase for the Setup URI is:",
|
||||
generated.setupPassphrase,
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
// does not load the PouchDB browser adapter.
|
||||
export {
|
||||
decodeSettingsFromSetupURI,
|
||||
encodeSettingsToSetupURI,
|
||||
} from "npm:@vrtmrz/livesync-commonlib@0.1.0-rc.4/compat/API/processSetting";
|
||||
export { generateP2PRoomId } from "npm:@vrtmrz/livesync-commonlib@0.1.0-rc.4/compat/common/utils";
|
||||
export { upsertRemoteConfigurationInPlace } from "npm:@vrtmrz/livesync-commonlib@0.1.0-rc.4/remote-configurations";
|
||||
encodeTimeBoundSetupURI,
|
||||
isTimeBoundSetupURIUsableNow,
|
||||
} from "npm:@vrtmrz/livesync-commonlib@0.1.32-next.0/setup-uri";
|
||||
export type { TimeBoundSetupURIMode } from "npm:@vrtmrz/livesync-commonlib@0.1.32-next.0/setup-uri";
|
||||
export { generateP2PRoomId } from "npm:@vrtmrz/livesync-commonlib@0.1.32-next.0/compat/common/utils";
|
||||
export { upsertRemoteConfigurationInPlace } from "npm:@vrtmrz/livesync-commonlib@0.1.32-next.0/remote-configurations";
|
||||
export {
|
||||
createNewVaultSettings,
|
||||
DEFAULT_SETTINGS,
|
||||
@@ -14,5 +16,5 @@ export {
|
||||
PREFERRED_BASE,
|
||||
PREFERRED_JOURNAL_SYNC,
|
||||
PREFERRED_SETTING_SELF_HOSTED,
|
||||
} from "npm:@vrtmrz/livesync-commonlib@0.1.0-rc.4/settings";
|
||||
export type { ObsidianLiveSyncSettings } from "npm:@vrtmrz/livesync-commonlib@0.1.0-rc.4/settings";
|
||||
} from "npm:@vrtmrz/livesync-commonlib@0.1.32-next.0/settings";
|
||||
export type { ObsidianLiveSyncSettings } from "npm:@vrtmrz/livesync-commonlib@0.1.32-next.0/settings";
|
||||
|
||||
Reference in New Issue
Block a user