Skip to content

Cloud sync of profiles and preferences through byte.vet - #71

Open
SYM01 wants to merge 3 commits into
developfrom
feat/sync
Open

SYM01 wants to merge 3 commits into
developfrom
feat/sync

Conversation

@SYM01

@SYM01 SYM01 commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

Proxyverse can now sync profiles and preferences (theme, language, toolbar action) across browsers and devices with a byte.vet account. The protocol is in docs/sync-protocol.md, and the server's wire contract is vendored as docs/sync-api.openapi.yaml.

  • Sign-in: OAuth 2.1 code + PKCE through the browser's sign-in window (identity, a required permission with no install warning). Requests to byte.vet never send its cookies.
  • Engine: per-record last-writer-wins by hybrid logical clock, with tombstones, a full resync when the server's history is purged, per-device "This device only" profiles, and a choice on first sign-in (merge, use the account's data, or upload this device's). Pending changes survive service worker restarts.
  • When it syncs: at browser start, after sign-in, 2 s after a local change, every 15 min, and on "Sync now". A service worker wake with nothing pending doesn't contact the server.
  • UI: a Sync section on the Preference page with account, status ("Last synced 5 minutes ago"), Sync now, sync-preferences switch and snapshot restore; a "This device only" switch in the profile editor. The Preference page now has one aligned label column.
  • Fixes along the way: deleting the active profile (locally or by sync) now switches to Direct instead of keeping the deleted proxy applied; profile writes from different extension pages no longer overwrite each other.

Builds

PROXYVERSE_ENV selects the server at build time. Store builds always target https://byte.vet.

Command Output
npm run build / build:firefox Production (byte.vet), as before
npm run build:preview Chrome, against northernlight.byte.vet, fixed dev extension ID, in dist/
npm run build:preview:firefox Firefox, against northernlight, in dist-firefox/

Testing

  • Unit tests (npm run coverage): 408 pass, including two-device engine tests against a fake byte.vet.
  • Chromium e2e: 113 pass, including 8 new sync tests (real sign-in window against the fake server, push/pull, poll alarm, service worker restart, Direct fallback checked with real traffic, local-only, first-sync choice, snapshot restore). One unidentified test failed once in the first of four full runs and didn't recur.
  • Both builds and all type-checks pass.
  • Manually: signed in and synced in Chrome against northernlight.

Not run: the Firefox smoke suite (npm run test:e2e:firefox), which includes a new sync test; Firefox couldn't start in the agent's sandbox. The two-device live test (npm run test:e2e:live) needs a person to sign in twice.

Before release

  • Register the store redirect URIs on byte.vet's live proxyverse-extension client: the Chrome Web Store and Edge Add-ons IDs, plus the Firefox one already listed in build/README.md.
  • Translations: the new strings exist in English only, for Transifex.

🤖 Generated with Claude Code

SYM01 and others added 3 commits October 27, 2026 22:24
Draft v1 of the profile/preference sync protocol: OAuth 2.1 + PKCE with
byte.vet as authorization server, per-record last-writer-wins by hybrid
logical clock, push on change plus periodic pull, server snapshots for
restore, and per-device opt-out. Design only; nothing is implemented.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sync profiles and preferences (theme, language, toolbar action) across
browsers and devices with a byte.vet account, per docs/sync-protocol.md.

- Sign-in: OAuth 2.1 code + PKCE via identity.launchWebAuthFlow, run from
  the Preference page. Refresh tokens rotate under a cross-context lock;
  requests never send byte.vet cookies (its auth server refuses
  cookie-bearing POSTs).
- Engine: per-record last-writer-wins by hybrid logical clock, tombstones,
  full resync after cursor_expired, held records of unknown schemas,
  per-device "This device only" profiles, first-sign-in choice (merge,
  use cloud, upload). No outbox: pending changes survive service worker
  restarts.
- Background: sync at start, after sign-in, 2 s after a local change and
  every 15 min; typed sync:* messages for the UI.
- UI: Sync section on the Preference page, first-sync modal, snapshot
  restore, and the "This device only" switch in the profile editor.
- Build: PROXYVERSE_ENV (production, northernlight, e2e) picks the server
  and OAuth client; Chrome dev builds carry a dev key for a fixed ID.
  New scripts: build:preview, build:preview:firefox, sync:api-types,
  test:e2e:live. identity is a required permission (no warning).
- Contract: docs/sync-api.openapi.yaml vendored from the byte.vet webapp,
  client types generated from it, with a drift test.
- Fixes: deleting the active profile now falls back to Direct; profile
  writes are serialized across extension contexts.
- Tests: unit tests for every sync module, two-device engine tests against
  a fake byte.vet, Chromium e2e for sign-in, push/pull, alarms, restarts,
  local-only and restore, a Firefox smoke test, and an opt-in live smoke
  against northernlight.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UX
- Preference page: one shared label column across sections; Import and
  Export as labeled rows; the signed-out Sync section as an Account row;
  Sign out as a text button.
- Last sync shown as relative time ("a few seconds ago", "5 minutes ago"),
  with the exact time on hover.
- Popup Config button opens Preference when no custom profile is active.
- Sync privacy note reworded: data is stored encrypted and used only to
  keep devices in sync (byte.vet holds the keys; not end-to-end).

Behavior
- A service worker wake (most page loads) no longer reaches the server
  when nothing is pending and the last sync is recent; the poll alarm
  pulls the rest.
- One cookie-free fetch for every byte.vet request (syncFetch); the fake
  byte.vet sets a session cookie and refuses cookie-bearing POSTs, so e2e
  catches a regression.
- Sync settings written under a lock; sign-out removes its keys
  (Host.remove) instead of writing null.

Simplification
- needsFirstChoice flag instead of a three-state phase; fewer state
  writes per run; one decodeRemote; pref defaults owned by their modules;
  shared identity implementation in BaseAdapter; build settings from one
  source; dead API surface removed.
- Shared unit-test fixtures (tests/helpers.ts) and e2e helpers
  (signedInStorage, popconfirmOK); the fake server uses the generated
  contract types.

Build
- build:preview:firefox writes to dist-firefox/, so it no longer replaces
  the Chrome preview in dist/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant