From a56507080913c752b44cca9055dee01b652a9002 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Tue, 15 Sep 2026 18:28:20 +0000 Subject: [PATCH] Use profile storage for TURN source settings --- docs/design_docs/renewable_turn_credentials.md | 18 ++++++++++-------- package-lock.json | 8 ++++---- package.json | 2 +- .../browser/BrowserP2PTransportSettings.svelte | 11 ++++++----- .../messages/LiveSyncProvisionalMessages.ts | 1 - src/common/reportTool.unit.spec.ts | 5 +---- src/common/turnSettingsPrivacy.ts | 17 ++++++++++++----- src/features/P2PSync/TurnConfiguration.svelte | 10 ++++------ src/integrations/iceServerSources.ts | 11 ----------- src/integrations/iceServerSources.unit.spec.ts | 8 -------- .../SetupWizard/dialogs/SetupRemoteP2P.svelte | 8 ++++---- 11 files changed, 42 insertions(+), 57 deletions(-) diff --git a/docs/design_docs/renewable_turn_credentials.md b/docs/design_docs/renewable_turn_credentials.md index 958438e9..cdef0ae8 100644 --- a/docs/design_docs/renewable_turn_credentials.md +++ b/docs/design_docs/renewable_turn_credentials.md @@ -1,6 +1,6 @@ --- date: 2026-09-15 -commonlib-version: "0.1.25-dev.turn-credentials.4" +commonlib-version: "0.1.25-dev.turn-credentials.5" self-hosted-livesync-version: "1.0.28" status: unreleased --- @@ -143,10 +143,12 @@ and expired runtime credentials through the existing room replacement policy. The API token is an ordinary sensitive connection setting. Persist it with the profile so that restarting a device and configuring another device do not require re-entry. This does not claim operating-system keychain storage. -When optional configuration encryption is enabled, cover both the saved -profile URI and any top-level settings projection containing the source. -Failure to encrypt either copy must leave the prior saved settings intact -and report a safe error; it must not silently save a plaintext replacement. +Persist the source only in the ordinary P2P profile URI, covered by the +existing optional configuration encryption. The top-level source is an +in-memory and sharing projection restored when the selected profile is +activated. A source draft without a Group ID is not persisted. Failure to +encrypt a managed profile must leave the prior saved settings intact and +report a safe error; it must not silently save a plaintext replacement. | Destination | Provider API token | Issued TURN username and credential | | --- | --- | --- | @@ -419,9 +421,9 @@ manual TURN when an explicitly configured source is unsupported. Persist the ordinary P2P settings projection alongside its profile. Keep the Group ID, enabled state, and autostart preference consistent with the current -settings. Optional configuration encryption continues to protect both the -profile and its source configuration. Issued credentials never populate the -persisted manual TURN fields. +settings. The source configuration is persisted only inside the profile URI +and restored by the existing profile activation. Issued credentials never +populate the persisted manual TURN fields. The P2P data protocol and Group ID remain unchanged. A peer using manually configured TURN can communicate with one using issued credentials; validate diff --git a/package-lock.json b/package-lock.json index 4c6e006d..a8343088 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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": "file:.devmemo.local/packages/vrtmrz-livesync-commonlib-0.1.25-dev.turn-credentials.4.tgz", + "@vrtmrz/livesync-commonlib": "file:.devmemo.local/packages/vrtmrz-livesync-commonlib-0.1.25-dev.turn-credentials.5.tgz", "@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.25-dev.turn-credentials.4", - "resolved": "file:.devmemo.local/packages/vrtmrz-livesync-commonlib-0.1.25-dev.turn-credentials.4.tgz", - "integrity": "sha512-q8jFJYzBzp7fsE+fU48A8gTvQ5DnJexkhReV8hUu2vhZvlV0e2ywUpPxkzx5KNNwSVpLSX96JCHRBLUvDMXKUQ==", + "version": "0.1.25-dev.turn-credentials.5", + "resolved": "file:.devmemo.local/packages/vrtmrz-livesync-commonlib-0.1.25-dev.turn-credentials.5.tgz", + "integrity": "sha512-mAKjoJoXKMl1ts8mUTvYsjJy4bluGykBCvwXDgjtY7cLrG6ct0iRrlyVrLs7GY7xnVnWeNmu/vj12D9IMzEpCQ==", "license": "MIT", "dependencies": { "@aws-sdk/client-s3": "^3.808.0", diff --git a/package.json b/package.json index 3506aa9d..3ff89673 100644 --- a/package.json +++ b/package.json @@ -181,7 +181,7 @@ "@smithy/types": "^4.14.3", "@smithy/util-retry": "^4.4.5", "@vrtmrz/browser-ui-kit": "0.1.0", - "@vrtmrz/livesync-commonlib": "file:.devmemo.local/packages/vrtmrz-livesync-commonlib-0.1.25-dev.turn-credentials.4.tgz", + "@vrtmrz/livesync-commonlib": "file:.devmemo.local/packages/vrtmrz-livesync-commonlib-0.1.25-dev.turn-credentials.5.tgz", "@vrtmrz/obsidian-plugin-kit": "0.1.4", "@vrtmrz/ui-interactions": "0.1.2", "diff-match-patch": "^1.0.5", diff --git a/src/apps/browser/BrowserP2PTransportSettings.svelte b/src/apps/browser/BrowserP2PTransportSettings.svelte index 2d13a081..6f332cda 100644 --- a/src/apps/browser/BrowserP2PTransportSettings.svelte +++ b/src/apps/browser/BrowserP2PTransportSettings.svelte @@ -5,23 +5,24 @@ import type { P2PSyncSetting } from "@vrtmrz/livesync-commonlib/compat/common/types"; import type { P2PReplicatorPaneHost } from "@/features/P2PSync/P2PReplicator/P2PReplicatorPaneHost"; import TurnConfiguration from "@/features/P2PSync/TurnConfiguration.svelte"; - import { validateTurnSettings } from "@/integrations/iceServerSources"; + import { validateIceServerSourceConfiguration } from "@/integrations/iceServerSources"; let { host }: { host: P2PReplicatorPaneHost } = $props(); const currentSettings = () => host.services.setting.currentSettings() as P2PSyncSetting; function turnSettings(settings: P2PSyncSetting) { return { + P2P_roomID: settings.P2P_roomID, P2P_turnServers: settings.P2P_turnServers, P2P_turnUsername: settings.P2P_turnUsername, P2P_turnCredential: settings.P2P_turnCredential, P2P_iceServerSource: structuredClone(settings.P2P_iceServerSource), - encryptedP2PIceServerSource: settings.encryptedP2PIceServerSource, }; } let draft = $state(turnSettings(currentSettings())); let saved = $state(JSON.stringify(turnSettings(currentSettings()))); const isModified = $derived(JSON.stringify(draft) !== saved); - const sourceError = $derived(validateTurnSettings(draft)); + const sourceError = $derived(validateIceServerSourceConfiguration(draft.P2P_iceServerSource)); + const sourceNeedsRoom = $derived(!!draft.P2P_iceServerSource && (draft.P2P_roomID ?? "").trim() === ""); function loadSettings(settings: P2PSyncSetting): void { const next = turnSettings(settings); @@ -31,7 +32,7 @@ onMount(() => host.services.context.events.onEvent("setting-saved", (settings) => loadSettings(settings as P2PSyncSetting))); async function save(): Promise { - if (sourceError) return; + if (sourceError || sourceNeedsRoom) return; const values = $state.snapshot(draft); await host.services.setting.updateSettings((settings) => { const next = { ...settings, ...values, remoteConfigurations: { ...settings.remoteConfigurations } }; @@ -52,7 +53,7 @@

Configure TURN only when a direct peer-to-peer connection cannot be established.

-