From 66233e5f8a6cb84880941e9d93f5c6c1d31c8122 Mon Sep 17 00:00:00 2001 From: Owen McGirr Date: Tue, 6 Oct 2026 19:36:51 +0100 Subject: [PATCH 1/4] Document accounts, settings sync and their build variables Add an Accounts and settings sync section to the README: what syncs and what never does, how conflicts are handled, where secrets and sync state live, the two release build variables, running a dev build against an account project, and the local-stack integration tests. List the new variables with the other release configuration in the README and the macOS release guide. Refs #987 Co-Authored-By: Claude Opus 5.5 (1M context) --- README.md | 28 +++++++++++++++++++++++++++- docs/macos-releases.md | 2 ++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 97820c5f..430f8342 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Packaged release builds check a dedicated Tauri feed at `update-feed/latest.json The `Release Switchify PC` workflow accepts existing `v` tags, renders release-only updater configuration, and publishes signed Apple-silicon macOS and x64 Windows artifacts into one GitHub release. It updates the dedicated feed only after both platform packages pass verification. It does not alter the public C# `v0.10.0` release or its `latest.yml` feed. -Before the workflow can run, configure `TAURI_UPDATER_PUBLIC_KEY`, `APPLE_SIGNING_IDENTITY`, and `CERTUM_CERT_THUMBPRINT` as repository variables; configure the Tauri updater private key/password and platform signing credentials as protected secrets. The Windows job targets the self-hosted signing runner with SimplySign available. Private signing material is never generated by or committed to this repository. +Before the workflow can run, configure `TAURI_UPDATER_PUBLIC_KEY`, `APPLE_SIGNING_IDENTITY`, `CERTUM_CERT_THUMBPRINT`, `SWITCHIFY_SUPABASE_URL`, and `SWITCHIFY_SUPABASE_PUBLISHABLE_KEY` as repository variables; configure the Tauri updater private key/password and platform signing credentials as protected secrets. The Windows job targets the self-hosted signing runner with SimplySign available. Private signing material is never generated by or committed to this repository. ## Diagnostics @@ -80,6 +80,32 @@ Protocol v1 clients may attach an optional `deviceName` to an authenticated `con Anonymous diagnostic telemetry is disabled until the user explicitly opts in. Opt-in creates an opaque installation UUID and permits best-effort health reports plus sanitized error reports; retryable error reports are bounded to 20, and opting out deletes the identifier and queue immediately. Builds expose telemetry only when `SWITCHIFY_TELEMETRY_ENDPOINT` is an HTTPS endpoint and `TIMBERLOGS_API_KEY` is supplied from release configuration. Neither value is committed to the repository. See the [privacy policy](https://switchifyapp.com/privacy). +## Accounts and settings sync + +Signing in under Settings → Account (or the account entry at the bottom of the sidebar) brings a user's settings to their other computers. It uses the same Supabase accounts as Switchify on Android: the user enters their email and the 6-digit code it receives; there is no password. The schema, migrations, and database tests live in the private `switchifyapp/switchify-supabase` repository. + +- **What syncs:** pointer, repeat, dwell and cursor settings; custom switch profiles; switch bindings including their keys; point scan and scan preferences; keyboard layout; and remote switch slots. The document is versioned and parsed strictly; a copy written by a newer Switchify PC is never overwritten, and this install asks to be updated instead. +- **What never syncs:** the BLE desktop ID, paired devices and pairing tokens, telemetry consent and install ID, setup progress, and start with system. +- **How:** changes upload a few seconds after they settle, other computers' changes are checked every five minutes and on Sync now, and every write is conditional on the server-owned revision, so concurrent changes merge by section (profiles by ID) instead of overwriting each other. The first sync on a computer whose settings differ from the account asks which to keep. +- **Secrets:** requests are made from Rust, so the webview's content security policy stays closed. The refresh token is kept in the OS keychain (service `com.enaboapps.switchify.pc.account`); access tokens stay in memory. Tokens and codes are never logged or sent to the webview. The merge base is kept in `settings-sync.json` in the application configuration folder and contains no secrets. + +Accounts are compiled in from two build-time variables, which release builds require: + +| Name | Value | +| --- | --- | +| `SWITCHIFY_SUPABASE_URL` | `https://.supabase.co` (must be HTTPS) | +| `SWITCHIFY_SUPABASE_PUBLISHABLE_KEY` | The project's `sb_publishable_…` key | + +Both are public values (the Android app embeds them too) and are set as repository variables. Builds without them, including ordinary development builds, show "Accounts are unavailable in this build" and keep the version in the sidebar. To try accounts locally, export both variables before `npm run tauri dev`; note that a dev build signs in against whichever project they name. + +Automated tests use fake transports and never touch the network or the keychain. Two ignored tests run end to end against a local stack from `switchify-supabase` (`supabase start`), reading sign-in codes from its Mailpit inbox: + +```bash +SWITCHIFY_LOCAL_SUPABASE_URL=http://127.0.0.1:54321 \ +SWITCHIFY_LOCAL_SUPABASE_KEY= \ +cargo test --manifest-path src-tauri/Cargo.toml local_stack -- --ignored +``` + ## Windows UIAccess package Windows grants UIAccess only to a trusted, signed executable installed in a secure location. Sign in to SimplySign Desktop, expose the Certum code-signing certificate, and set its thumbprint before packaging: diff --git a/docs/macos-releases.md b/docs/macos-releases.md index 19a72108..5d95a773 100644 --- a/docs/macos-releases.md +++ b/docs/macos-releases.md @@ -36,6 +36,8 @@ Configure these environment variables: | `APPLE_SIGNING_IDENTITY` | Full `Developer ID Application: … (TEAMID)` identity | | `APPLE_TEAM_ID` | Apple Developer team ID from the identity | | `TIMBERLOGS_ENDPOINT` | Production HTTPS telemetry endpoint | +| `SWITCHIFY_SUPABASE_URL` | Supabase project URL for accounts and settings sync (HTTPS) | +| `SWITCHIFY_SUPABASE_PUBLISHABLE_KEY` | Supabase publishable key (public, `sb_publishable_…`) | Configure `TAURI_UPDATER_PUBLIC_KEY` as a repository variable. Keep an encrypted offline backup of the updater private key and password: installed applications cannot accept future updates if that key is lost. From de32680438eb6e012b884d5c4197c44297d334af Mon Sep 17 00:00:00 2001 From: Owen McGirr Date: Tue, 6 Oct 2026 19:43:18 +0100 Subject: [PATCH 2/4] Clarify sync conflict wording and the local-stack tests Address review: say that the account's copy wins a section changed on both computers, and that only the account test reads Mailpit (with its URL variable) while the sync test signs up with a password. Co-Authored-By: Claude Opus 5.5 (1M context) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 430f8342..011d6d9f 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ Signing in under Settings → Account (or the account entry at the bottom of the - **What syncs:** pointer, repeat, dwell and cursor settings; custom switch profiles; switch bindings including their keys; point scan and scan preferences; keyboard layout; and remote switch slots. The document is versioned and parsed strictly; a copy written by a newer Switchify PC is never overwritten, and this install asks to be updated instead. - **What never syncs:** the BLE desktop ID, paired devices and pairing tokens, telemetry consent and install ID, setup progress, and start with system. -- **How:** changes upload a few seconds after they settle, other computers' changes are checked every five minutes and on Sync now, and every write is conditional on the server-owned revision, so concurrent changes merge by section (profiles by ID) instead of overwriting each other. The first sync on a computer whose settings differ from the account asks which to keep. +- **How:** changes upload a few seconds after they settle, other computers' changes are checked every five minutes and on Sync now, and every write is conditional on the server-owned revision, so concurrent changes merge by section (profiles by ID) instead of overwriting each other; if both computers change the same section (or profile), the account's copy wins, though an edit is never lost to a deletion. The first sync on a computer whose settings differ from the account asks which to keep. - **Secrets:** requests are made from Rust, so the webview's content security policy stays closed. The refresh token is kept in the OS keychain (service `com.enaboapps.switchify.pc.account`); access tokens stay in memory. Tokens and codes are never logged or sent to the webview. The merge base is kept in `settings-sync.json` in the application configuration folder and contains no secrets. Accounts are compiled in from two build-time variables, which release builds require: @@ -98,7 +98,7 @@ Accounts are compiled in from two build-time variables, which release builds req Both are public values (the Android app embeds them too) and are set as repository variables. Builds without them, including ordinary development builds, show "Accounts are unavailable in this build" and keep the version in the sidebar. To try accounts locally, export both variables before `npm run tauri dev`; note that a dev build signs in against whichever project they name. -Automated tests use fake transports and never touch the network or the keychain. Two ignored tests run end to end against a local stack from `switchify-supabase` (`supabase start`), reading sign-in codes from its Mailpit inbox: +Automated tests use fake transports and never touch the network or the keychain. Two ignored tests run end to end against a local stack from `switchify-supabase` (`supabase start`). The account test reads its sign-in code from the stack's Mailpit inbox (`SWITCHIFY_LOCAL_MAILPIT_URL`, default `http://127.0.0.1:54324`); the sync test signs up test users with a password: ```bash SWITCHIFY_LOCAL_SUPABASE_URL=http://127.0.0.1:54321 \ From 7fbeeae6d15865797ecff66c7d77fccfcb966308 Mon Sep 17 00:00:00 2001 From: Owen McGirr Date: Tue, 6 Oct 2026 20:48:30 +0100 Subject: [PATCH 3/4] Point the sync docs at the Account page The account now has its own page opened from the sidebar rather than a Settings tab. Co-Authored-By: Claude Opus 5.5 (1M context) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 011d6d9f..4de04aa6 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Anonymous diagnostic telemetry is disabled until the user explicitly opts in. Op ## Accounts and settings sync -Signing in under Settings → Account (or the account entry at the bottom of the sidebar) brings a user's settings to their other computers. It uses the same Supabase accounts as Switchify on Android: the user enters their email and the 6-digit code it receives; there is no password. The schema, migrations, and database tests live in the private `switchifyapp/switchify-supabase` repository. +Signing in on the Account page (the account entry at the bottom of the sidebar) brings a user's settings to their other computers. It uses the same Supabase accounts as Switchify on Android: the user enters their email and the 6-digit code it receives; there is no password. The schema, migrations, and database tests live in the private `switchifyapp/switchify-supabase` repository. - **What syncs:** pointer, repeat, dwell and cursor settings; custom switch profiles; switch bindings including their keys; point scan and scan preferences; keyboard layout; and remote switch slots. The document is versioned and parsed strictly; a copy written by a newer Switchify PC is never overwritten, and this install asks to be updated instead. - **What never syncs:** the BLE desktop ID, paired devices and pairing tokens, telemetry consent and install ID, setup progress, and start with system. From 2acf0fdb3bed34cd6f833df303831daa8f074348 Mon Sep 17 00:00:00 2001 From: Owen McGirr Date: Tue, 6 Oct 2026 20:49:34 +0100 Subject: [PATCH 4/4] Describe builds without accounts accurately Without the build variables there is no Account page; the sidebar shows the version instead. Co-Authored-By: Claude Opus 5.5 (1M context) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4de04aa6..73c94fb2 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ Accounts are compiled in from two build-time variables, which release builds req | `SWITCHIFY_SUPABASE_URL` | `https://.supabase.co` (must be HTTPS) | | `SWITCHIFY_SUPABASE_PUBLISHABLE_KEY` | The project's `sb_publishable_…` key | -Both are public values (the Android app embeds them too) and are set as repository variables. Builds without them, including ordinary development builds, show "Accounts are unavailable in this build" and keep the version in the sidebar. To try accounts locally, export both variables before `npm run tauri dev`; note that a dev build signs in against whichever project they name. +Both are public values (the Android app embeds them too) and are set as repository variables. Builds without them, including ordinary development builds, have no Account page and show the version in the sidebar instead. To try accounts locally, export both variables before `npm run tauri dev`; note that a dev build signs in against whichever project they name. Automated tests use fake transports and never touch the network or the keychain. Two ignored tests run end to end against a local stack from `switchify-supabase` (`supabase start`). The account test reads its sign-in code from the stack's Mailpit inbox (`SWITCHIFY_LOCAL_MAILPIT_URL`, default `http://127.0.0.1:54324`); the sync test signs up test users with a password: